Reputation: 8554
I have bubble plot like this, and I am willing to put labels next to each bubble (their name). Does any body know how to do that?
Upvotes: 1
Views: 3082
Reputation: 13459
@Falko refered to another post that indicates you should be looking for the text
method of the axes. However, your problem is quite a bit more involved than that, because you'll need to implement an offset that scales dynamically with the size of the "bubble" (the marker). That means you'll be looking into the transformation methods of matplotlib.
As you didn't provide a simple example dataset to experiment with, I've used one that is freely available: earthquakes of 1974. In this example, I'm plotting the depth of the quake vs the date on which it occurred, using the magnitude of the earthquake as the size of the bubbles/markers. I'm appending the locations of where these earthquakes happened next to the markers, not inside (which is far more easy: ignore the offset and set ha='center'
in the call to ax.text
).
Note that the bulk of this code example is merely about getting some dataset to toy with. What you really needed was just the ax.text
method with the offset.
from __future__ import division # use real division in Python2.x
from matplotlib.dates import date2num
import matplotlib.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# Get a dataset
data_url = 'http://datasets.flowingdata.com/earthquakes1974.csv'
df = pd.read_csv(data_url, parse_dates=['time'])
# Select a random subset of that dataframe to generate some variance in dates, magnitudes, ...
data = np.random.choice(df.shape[0], 10)
records = df.loc[data]
# Taint the dataset to add some bigger variance in the magnitude of the
# quake to show that the offset varies with the size of the marker
records.mag.values[:] = np.arange(10)
records.mag.values[0] = 50
records.mag.values[-1] = 100
dates = [date2num(dt) for dt in records.time]
f, ax = plt.subplots(1,1)
ax.scatter(dates, records.depth, s=records.mag*100, alpha=.4) # markersize is given in points**2 in recentt versions of mpl
for _, record in records.iterrows():
# Specify an offset for the text annotation:
# it is approx the radius of the disc + 10 points to the right
dx, dy = np.sqrt(record.mag*100)/f.dpi/2 + 10/f.dpi, 0.
offset = transforms.ScaledTranslation(dx, dy, f.dpi_scale_trans)
ax.text(date2num(record.time), record.depth, s=record.place,
va='center', ha='left',
transform=ax.transData + offset)
ax.set_xticks(dates)
ax.set_xticklabels([el.strftime("%Y-%M") for el in records.time], rotation=-60)
ax.set_ylabel('depth of earthquake')
plt.show()
For one such run, I got:
Definitely not pretty because of the overlapping labels, but it was just an example to show how to use the transforms
in matplotlib to dynamically add an offset to the labels.
Upvotes: 4