Can't draw circle with right proportions Matplotlib Python

Viewed 165

I want to draw Circle on my plot. For this purpose I decided to use patch.Circle class from matplotlib. Cirlce object uses radius argument to set a radius of a circle, but if the axes ratio is not 1 (see my plot), how to draw circle with right proportions? My code for drawing circle is:

rect = patches.Circle(xy=(9, yaxes),radius= 2, linewidth=3, edgecolor='r', facecolor='red',alpha=0.5)
ax.add_patch(rect)

yaxes is equal 206 in this example (because I wanted to draw it upper left coner). Here is a picture I got using this code: enter image description here

But I want something like this: enter image description here

2 Answers

You could use ax.transData to transform 1,1 vs 0,0 and obtain the deformation in x vs y direction. That ratio can be used to know the horizontal versus the vertical size of the circle.

If you just need to place a circle using coordinates relative to the axes, plt.scatter with transform=ax.transAxes can be used. Note that the size is an "area" measure based on "points" (a "point" is 1/72th of an inch).

The following example code uses the data coordinates to position the "circle" (using an ellipse) and the x-coordinates for the radius. A red circle is placed using axes coordinates.

from matplotlib import pyplot as plt
from matplotlib.patches import Ellipse
import pandas as pd
import numpy as np

# plot some random data
np.random.seed(2021)
df = pd.DataFrame({'y': np.random.normal(10, 100, 50).cumsum() + 2000},
                  index=np.arange(101, 151))
ax = df.plot(figsize=(12, 5))
# find an "interesting" point
max_ind = df['y'].argmax()
max_x = df.index[max_ind]
max_y = df.iloc[max_ind]['y']

# calculate the aspect ratio
xscale, yscale = ax.transData.transform([1, 1]) - ax.transData.transform([0, 0])

# draw the ellipse to be displayed as circle
radius_x = 4
radius_y = radius_x * xscale / yscale
ax.add_patch(Ellipse((max_x, max_y), radius_x, radius_y, color='purple', alpha=0.4))

# use ax.scatter to draw a red dot at the top left
ax.scatter(0.05, 0.9, marker='o', s=2000, color='red', transform=ax.transAxes)

plt.show()

drawing a circle with radius in x coordinates

Some remarks about drawing the ellipse:

  • this will only work for linear coordinates, not e.g. for logscale or polar coordinates
  • the code supposes nor the axis limits nor the axis position will change afterwards, as these will distort the aspect ratio

The issue seems to be that your X (passed to xy=) is not always the same as your Y, thus the oval instead of a perfect circle.

Related