Reputation: 1565
If you want to label your plot points using python matplotlib, I used the following code.
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
A = anyarray
B = anyotherarray
plt.plot(A,B)
for i,j in zip(A,B):
ax.annotate('%s)' %j, xy=(i,j), xytext=(30,0), textcoords='offset points')
ax.annotate('(%s,' %i, xy=(i,j))
plt.grid()
plt.show()
I know that xytext=(30,0)
goes along with the textcoords
and you use those 30,0 values to position the data label point, so it's on the y=0
and x=30
on its own little area.
You need both the lines plotting i
and j
otherwise you only plot x
or y
data label.
You get something like this out (note the labels only):
It's not ideal, there is still some overlap.
Upvotes: 100
Views: 242768
Reputation: 23131
If arrows are not needed, text()
can also be used to label points.
import matplotlib.pyplot as plt
A = [-0.75, -0.25, 0, 0.25, 0.5, 0.75, 1.0]
B = [0.73, 0.97, 1.0, 0.97, 0.88, 0.73, 0.54]
fig, ax = plt.subplots()
ax.plot(A,B)
for x, y in zip(A, B):
ax.text(x, y, f"({x}, {y})", fontsize=8)
You can also annotate some points or change position of the labels relative to the point by conditionally annotating points. Also, you can assign arbitrary labels.
For example, the following code draws the labels on the left side of the point if x>0
and on the right side otherwise. Also, annotate()
admits additional kwargs which can be used to beautify the labels.
A = -0.75, -0.25, 0, 0.25, 0.5, 0.75, 1.0
B = 0.73, 0.97, 1.0, 0.97, 0.88, 0.73, 0.54
labels = 'ABCDEFG'
fig, ax = plt.subplots()
ax.plot(A,B)
# annotator function that draws a label and an arrow
# that points from the label to its corresponding point
def annotate(ax, label, x, y, xytext):
ax.annotate(label, xy=(x,y),
xytext=xytext, textcoords='offset points',
fontsize=15,
arrowprops={'arrowstyle': '-|>', 'color': 'black'})
# conditionally position labels
for label, x, y in zip(labels, A, B):
if y > 0.9:
annotate(ax, label, x, y, (-5, -40))
else:
annotate(ax, label, x, y, (-5, 30))
Upvotes: 2
Reputation: 369074
How about print (x, y)
at once.
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
A = -0.75, -0.25, 0, 0.25, 0.5, 0.75, 1.0
B = 0.73, 0.97, 1.0, 0.97, 0.88, 0.73, 0.54
ax.plot(A,B)
for xy in zip(A, B): # <--
ax.annotate('(%s, %s)' % xy, xy=xy, textcoords='data') # <--
ax.grid()
plt.show()
Upvotes: 123
Reputation: 10606
I had a similar issue and ended up with this:
For me this has the advantage that data and annotation are not overlapping.
from matplotlib import pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
A = -0.75, -0.25, 0, 0.25, 0.5, 0.75, 1.0
B = 0.73, 0.97, 1.0, 0.97, 0.88, 0.73, 0.54
plt.plot(A,B)
# annotations at the side (ordered by B values)
x0,x1=ax.get_xlim()
y0,y1=ax.get_ylim()
for ii, ind in enumerate(np.argsort(B)):
x = A[ind]
y = B[ind]
xPos = x1 + .02 * (x1 - x0)
yPos = y0 + ii * (y1 - y0)/(len(B) - 1)
ax.annotate('',#label,
xy=(x, y), xycoords='data',
xytext=(xPos, yPos), textcoords='data',
arrowprops=dict(
connectionstyle="arc3,rad=0.",
shrinkA=0, shrinkB=10,
arrowstyle= '-|>', ls= '-', linewidth=2
),
va='bottom', ha='left', zorder=19
)
ax.text(xPos + .01 * (x1 - x0), yPos,
'({:.2f}, {:.2f})'.format(x,y),
transform=ax.transData, va='center')
plt.grid()
plt.show()
Using the text argument in .annotate
ended up with unfavorable text positions.
Drawing lines between a legend and the data points is a mess, as the location of the legend is hard to address.
Upvotes: 10