Search code examples
matplotlibannotationssubplotaxes

Problem with text and annotation x and y coordinates changing while looping through subplots matplotlib


I would like to iterate through subplots, plot data, and annotate the subplots with either the text function or the annotation function in matplotlib. Both functions ask for x and y coordinates in order to place text or annotations. I can get this to work fine, until I plot data. Then the annotations and the text jump all over the place and I cannot figure out why.

My set up is something like this, which produces well-aligned annotations with no data:

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

fig, ax=plt.subplots(nrows=3, ncols=3, sharex=True)
fig.suptitle('Axes ylim unpacking error demonstration')
annotation_colors=["red", "lightblue", "tan", "purple", "lightgreen", "black", "pink", "blue", "magenta"]

for jj, ax in enumerate(ax.flat):
    bott, top = plt.ylim()
    left, right = plt.xlim()
    ax.text(left+0.1*(right-left), bott+0.1*(top-bott), 'Annotation', color=annotation_colors[jj])

plt.show

When I add random data (or my real data), the annotations jump:

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

#Same as above but but with 9 random data frames plotted.
df_cols = ['y' + str(x) for x in range(1,10)]
df=pd.DataFrame(np.random.randint(0,10, size=(10,9)), columns=df_cols)
df['x']=range(0,10)

#Make a few columns much larger in terms of magnitude of mean values
df['y2']=df['y2']*-555
df['y5']=df['y5']*123

fig, ax=plt.subplots(nrows=3, ncols=3, sharex=True)
fig.suptitle('Axes ylim unpacking error demonstration')
annotation_colors=["red", "lightblue", "tan", "purple", "lightgreen", "black", "pink", "blue", "magenta"]

for jj, ax in enumerate(ax.flat):
    ax.plot(df['x'], df['y'+str(jj+1)], color=annotation_colors[jj])
    bott, top = plt.ylim()
    left, right = plt.xlim()
    ax.text(left+0.1*(right-left), bott+0.1*(top-bott), 'Annotation', color=annotation_colors[jj])

plt.show()

This is just to demonstrate the issue that is likely caused by my lack of understanding of how the ax and fig calls are working. It seems to me that the coordinates x and y of the ax.text call may actually apply to the coordinates of of the fig, or something similar. The end result is far worse with my actual data!!! In that case, some of the annotations end up miles above the actual plots and not even within the coordinates of any of the subplot axes. Others completely overlap! What I am misunderstanding?


Edit for more details:

I have tried Stef's solution of using axes coordinates of axes.text(0.1, 0.1, 'Annotation'...)

I get the following plot, which still shows the same problem of moving the text all over the place. Because I am running this example with random numbers, the annotations are moving randomly with every run - i.e. they are not just displaced in the subplots with different axis ranges (y2 and y5). Plot from code above that shows misplaced annotations


Solution

  • You can specify the text location in axes coordinates (as opposed to data coordinates as you did implicitely):

    ax.text(.1, .1, 'Annotation', color=annotation_colors[jj], transform=ax.transAxes)
    

    enter image description here

    See the Transformations Tutorial for further information.