4

I am trying to annotate a scatter plot in Python 2.7, with Matplotlib. Here is the plot code:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.rand(5,3), columns=list('ABC'))
df.insert(0,'Annotation_Text',['ABC','DEF','GHI','JKL','mnop'])

q = 2
pqr = 1

# Scatter Plot:
x = df['A']
y = df.iloc[:,q]
plt.scatter(x, y, marker='o', label = df.columns.tolist()[q])

# Plot annotation:
plt.annotate(df.iloc[pqr,0]+', (%.2f, %.2f)' % (x.ix[pqr],y.ix[pqr]), xy=(x.ix[pqr], y.ix[pqr]), xycoords='data', xytext = (x.ix[pqr], y.ix[pqr]), textcoords='offset points', arrowprops=dict(arrowstyle='-|>'))

# Axes title/legend:
plt.xlabel('xlabel', fontsize=18)
plt.ylabel('ylabel', fontsize=16)
plt.legend(scatterpoints = 1)

plt.show()

As you can see, the main line is the line starting with plt.annotate(df.iloc[pqr,0]+', (%................

I think that the main problem is with this part of the plt.annotate() line: xytext = (x.ix[pqr], y.ix[pqr]), textcoords='offset points', arrowprops=dict(arrowstyle='-|>'). From here, xytext = (x.ix[pqr], y.ix[pqr]) is just a tuple of the x and y co-ordinates of the data point to be annotated. Unfortunately, this is placing the annotation right at the data point, which I do not want. I want to leave some blank space between the data point and the annotation text.

Also, I am having a problem with the arrow and annotation that this line is producing. See below. Annotation Image

Problems:

  • Currently, the annotation text is overlapping the arrow. The annotation text is too close to the data point. I don't want it to be so close.
  • Also the arrow is pointing from right to left. I don't think I asked it to plot the arrow from right-to-left, so I don't know why it is plotting the arrow in this direction.

Is there a way to control the text annotation so that there is no overlap with the data point? Also, how do I change the arrow direction from right-to-left to the either a) the best direction or b) from left-to-right?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
edesz
  • 11,756
  • 22
  • 75
  • 123

1 Answers1

4

In plt.annotate(...), xy gives the position of the data you want to point at (one end of the arrow) and xytext gives the position of your text. In your code, they are overlapping because you specified the same positions for xy and xytext. Try this instead (for example):

plt.annotate(df.iloc[pqr,0]+', (%.2f, %.2f)' % (x.ix[pqr], y.ix[pqr]), xy=(x.ix[pqr], y.ix[pqr]), xycoords='data', xytext=(x.ix[pqr], y.ix[pqr]+0.3), arrowprops=dict(arrowstyle='-|>'), horizontalalignment='center')

The arrow by default is pointing from the text to the data point. If you want to change the direction of the arrow, you can use arrowprops=dict(arrowstyle='<|-')

Julien Spronck
  • 15,069
  • 4
  • 47
  • 55
  • Thank you. So, regarding `xytext=(,)`, if I try this `xy=(10,10),xytext=(10-0.1,10-0.1)`, does this mean that the annotation text starts at `(9.9,9.9)` or `(0.099,0.099)`? .i.e. what are the units of `xytext=(,)` and what are the units of `xy=(,)`? – edesz Jun 04 '15 at 17:01
  • 1
    By default `xycoords` and `textcoords` should be `data`. So, in this case, the text will be at (9.9, 9.9). You can change that if that is not your desired coordinates (if you want your text to follow your data, `data` seems like the logical choice). – Julien Spronck Jun 04 '15 at 17:06
  • @JulienSpronck could I ask you please what you mean by "data"? I am trying to give xytext and xy the same values just to see if I get an arrow that overlaps on the chart but I get an error when I do this. When I set xytext =0 the chart plots without error but I don't see any arrow. I am using ax1.annotate("", xy=(1999, 12000000), xytext=(0, 0),arrowprops=dict(arrowstyle="->")) and the ax1 axis runs between 800,000 and 1,900,000. Any help most welcome! – ZakS May 24 '18 at 14:37