How to prevent a matplotlib annotation being clipped by other axes

Question:

I have a plot in matplotlib with multiple subplots(axes), and I want to annotate the points within the axes. However, subsequent axes overlay annotations from previous axes (eg annotation on subplot(4,4,1) goes under subplot(4,4,2)). I have set the annotation zorder nice and high, but to no avail :/

I’ve used a modified version of Joe Kington’s awesome DataCursor for the annotations.

Any help would be greatly appreciated

Here’s an example:
enter image description here

Asked By: Josha Inglis

||

Answers:

One way to do it is to pop the text created by annotate out of the axes and add it to the figure. This way it will be displayed on top of all of the subplots.

As a quick example of the problem you’re having:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=5, ncols=5)
plt.setp(axes.flat, xticks=[], yticks=[], zorder=0)

ax = axes[0,0]
ax.annotate('Testing this out and seeing what happens', xy=(0.5, 0.5), 
            xytext=(1.1, .5), textcoords='axes fraction', zorder=100)

plt.show()

enter image description here

If we just pop the text object out of the axes and add it to the figure instead, it will be on top:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=5, ncols=5)
plt.setp(axes.flat, xticks=[], yticks=[], zorder=0)

ax = axes[0,0]
ax.annotate('Testing this out and seeing what happens', xy=(0.5, 0.5), 
            xytext=(1.1, .5), textcoords='axes fraction', zorder=100)

fig.texts.append(ax.texts.pop())

plt.show()

enter image description here

You mentioned the DataCursor snippet, and there you’d want to change the annotate method:

def annotate(self, ax):
    """Draws and hides the annotation box for the given axis "ax"."""
    annotation = ax.annotate(self.template, xy=(0, 0), ha='right',
            xytext=self.offsets, textcoords='offset points', va='bottom',
            bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
            arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')
            )
    # Put the annotation in the figure instead of the axes so that it will be on
    # top of other subplots.
    ax.figure.texts.append(ax.texts.pop())

    annotation.set_visible(False)
    return annotation

I haven’t tested the last bit, but it should work…

Answered By: Joe Kington

A quicker solution would be to change the zorder of the plots instead of actually bringing the text out of the plot itself (which by the way didn’t work for me).

So for this case in which the annotations extend to the right/bottom:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=5, ncols=3)

num_plots = len(axes) * len(axes[0])

for row in axes:
    for column in row:
        column.zorder = num_plots
        num_plots -= 1

Each following plot (left to right/up to bottom) inside the figure would be "behind" the previous one, so the annotations are not overlapped.

Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.