pythonmatplotlibannotate

Draw ellipses to annotate plot


I have recently discovered matplotlib as a much better alternative to Matlab for plots. Unfortunately, my knowledge of python is close to zero.

Consider the following figure and the associated code.

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

plt.figure(figsize=[3.3, 3.3])
plt.rcParams.update({'font.size': 8, 'text.usetex': True})

plt.plot([1, 2, 3, 4], [1, 4, 9, 16], color='r')
plt.plot([1, 2, 3, 4], [2, 5, 10, 17], color='r')
plt.plot([1, 2, 3, 4], [11, 14, 19, 26], color='b')
plt.plot([1, 2, 3, 4], [12, 15, 20, 27], color='b')

plt.annotate('blue curves', xy=(1.5, 22.5), xytext=(1.5, 22.5), ha='center', va='center')
plt.annotate('', xy=(2, 15), xytext=(1.5, 22), arrowprops=dict(width=0.1, headwidth=2, headlength=2, color='grey'))
plt.annotate('red curves', xy=(2.5, 22.5), xytext=(2.5, 22.5), ha='center', va='center')
plt.annotate('', xy=(3, 10), xytext=(2.5, 22), arrowprops=dict(width=0.1, headwidth=2, headlength=2, color='grey'))

plt.grid()
plt.xlabel('x')
plt.ylabel('y')

plt.savefig('filename.pdf', format='pdf')

plt.show()

enter image description here

I'd like to add a small ellipse/circle to each arrow that encloses the curves. The ellipse/circle should have the same style of the arrow (i.e., color, thicknes, etc.). Is there an easy way to do this without modifying much the existing code?

I tried to get some inspiration from this example but it didn't work.


Solution

  • While you can call plot and annotate on your plt-object, you will need to access the figure and the axis. A good explanation of the differences between plots, figures and axes is given here.

    The code you will have to add (before plt.show()):

    from matplotlib.patches import Ellipse
    
    fig = plt.gcf()  # access the current figure
    ax = plt.gca()   # access the current axis
    xmin, xmax = ax.get_xlim()  # get the minimum and maximum extent of the x-axis
    ymin, ymax = ax.get_ylim()  # get the minimum and maximum extent of the y-axis
    aspect_ratio = fig.get_figheight() / fig.get_figwidth()  # get aspect ratio, we will use it to scale the width and height of the ellipse to appear as a circle
    ax.add_patch(Ellipse(xy=(2, 15), width=(xmax-xmin)*aspect_ratio/5, height=(ymax-ymin)/5, color="grey", fill=False, lw=2))
    ax.add_patch(Ellipse(xy=(3, 10), width=(xmax-xmin)*aspect_ratio/5, height=(ymax-ymin)/5, color="grey", fill=False, lw=2))
    
    

    enter image description here