Inline Labels in Matplotlib

Inline labels in Matplotlib

Nice question, a while ago I've experimented a bit with this, but haven't used it a lot because it's still not bulletproof. I divided the plot area into a 32x32 grid and calculated a 'potential field' for the best position of a label for each line according the following rules:

  • white space is a good place for a label
  • Label should be near corresponding line
  • Label should be away from the other lines

The code was something like this:

import matplotlib.pyplot as plt
import numpy as np
from scipy import ndimage

def my_legend(axis = None):

if axis == None:
axis = plt.gca()

N = 32
Nlines = len(axis.lines)
print Nlines

xmin, xmax = axis.get_xlim()
ymin, ymax = axis.get_ylim()

# the 'point of presence' matrix
pop = np.zeros((Nlines, N, N), dtype=np.float)

for l in range(Nlines):
# get xy data and scale it to the NxN squares
xy = axis.lines[l].get_xydata()
xy = (xy - [xmin,ymin]) / ([xmax-xmin, ymax-ymin]) * N
xy = xy.astype(np.int32)
# mask stuff outside plot
mask = (xy[:,0] >= 0) & (xy[:,0] < N) & (xy[:,1] >= 0) & (xy[:,1] < N)
xy = xy[mask]
# add to pop
for p in xy:
pop[l][tuple(p)] = 1.0

# find whitespace, nice place for labels
ws = 1.0 - (np.sum(pop, axis=0) > 0) * 1.0
# don't use the borders
ws[:,0] = 0
ws[:,N-1] = 0
ws[0,:] = 0
ws[N-1,:] = 0

# blur the pop's
for l in range(Nlines):
pop[l] = ndimage.gaussian_filter(pop[l], sigma=N/5)

for l in range(Nlines):
# positive weights for current line, negative weight for others....
w = -0.3 * np.ones(Nlines, dtype=np.float)
w[l] = 0.5

# calculate a field
p = ws + np.sum(w[:, np.newaxis, np.newaxis] * pop, axis=0)
plt.figure()
plt.imshow(p, interpolation='nearest')
plt.title(axis.lines[l].get_label())

pos = np.argmax(p) # note, argmax flattens the array first
best_x, best_y = (pos / N, pos % N)
x = xmin + (xmax-xmin) * best_x / N
y = ymin + (ymax-ymin) * best_y / N

axis.text(x, y, axis.lines[l].get_label(),
horizontalalignment='center',
verticalalignment='center')

plt.close('all')

x = np.linspace(0, 1, 101)
y1 = np.sin(x * np.pi / 2)
y2 = np.cos(x * np.pi / 2)
y3 = x * x
plt.plot(x, y1, 'b', label='blue')
plt.plot(x, y2, 'r', label='red')
plt.plot(x, y3, 'g', label='green')
my_legend()
plt.show()

And the resulting plot:
Sample Image

Adding data labels to line graph in Matplotlib

In your code temp_labels is a list of lines, so x is a line object, which cannot be used to index a list, as indicated by the error. Start from here:

import matplotlib.pyplot as plt

dates = [10,11,12]
temp = [10,14,12]

plt.plot(dates,temp)

for x, y in zip(dates, temp):
label = y
plt.annotate(label, (x, y),
xycoords="data",
textcoords="offset points",
xytext=(0, 10), ha="center")

plt.show()

Sample Image

matplotlib plot Label along plot line

Below is an example simply to show how it can be done without consideration for appearance. For more information about annotating plots please see this detailed demonstration.

import matplotlib.pyplot as plt
x = [37, 45]; y = [-0.67778, -0.67778]

# as an example for where to place the text we can use the mean
xmean = sum(i for i in x) / float(len(x))
ymean = sum(i for i in y) / float(len(y))

plt.plot(x, y, '--k', lw=1.2)
plt.annotate('some text', xy=(xmean,ymean), xycoords='data')
plt.show() # or plt.savefig('filename.png')

Yields:

_soannotate.png

Creating labels where line appears in matplotlib figure

You can use something like

plt.axvline(10)
plt.text(10.1,0,'blah',rotation=90)

you might have to play around with the x and y value in text to get it to align properly.
You can find the more complete documentation here.



Related Topics



Leave a reply



Submit