Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is matplotlib plotting my circles as ovals?

Is there a way to get matplotlib to plot a perfect circle? They look more like ovals.

like image 380
user784756 Avatar asked Feb 10 '12 15:02

user784756


People also ask

How do I fix scale in matplotlib?

You can use the matplotlib. pyplot module's locator_params() method to change the axis scale. You can adjust the x-axis and y-axis at the same time with the code plt. locator_params(nbins = 10).


1 Answers

Just to expand on DSM's correct answer. By default, plots have more pixels along one axis over the other. When you add a circle, it's traditionally added in data units. If your axes have a symmetric range, that means one step along the x axis will involve a different number of pixels than one step along your y axis. So a symmetric circle in data units is asymmetric in your Pixel units (what you actually see).

As DSM correctly pointed out, you can force the x and y axes to have equal number of pixels per data unit. This is done using the plt.axis("equal") or ax.axis("equal") methods (where ax is an instance of an Axes).

You can also draw an Ellipse such that it is appropriately scaled to look like a circle on your plot. Here's an example of such a case:

import matplotlib.pyplot as plt from matplotlib.patches import Ellipse, Circle   fig = plt.figure() ax1 = fig.add_subplot(211) # calculate asymmetry of x and y axes: x0, y0 = ax1.transAxes.transform((0, 0)) # lower left in pixels x1, y1 = ax1.transAxes.transform((1, 1)) # upper right in pixes dx = x1 - x0 dy = y1 - y0 maxd = max(dx, dy) width = .15 * maxd / dx height = .15 * maxd / dy  # a circle you expect to be a circle, but it is not ax1.add_artist(Circle((.5, .5), .15)) # an ellipse you expect to be an ellipse, but it's a circle ax1.add_artist(Ellipse((.75, .75), width, height)) ax2 = fig.add_subplot(212)  ax2.axis('equal') # a circle you expect to be a circle, and it is ax2.add_artist(Circle((.5, .5), .15)) # an ellipse you expect to be an ellipse, and it is ax2.add_artist(Ellipse((.75, .75), width, height))  fig.savefig('perfectCircle1.png') 

resulting in this figure:

enter image description here

Alternatively, you can adjust your figure so that the Axes are square:

# calculate dimensions of axes 1 in figure units x0, y0, dx, dy = ax1.get_position().bounds maxd = max(dx, dy) width = 6 * maxd / dx height = 6 * maxd / dy  fig.set_size_inches((width, height))  fig.savefig('perfectCircle2.png') 

resulting in:

enter image description here

Notice how the second axes, which has the axis("equal") option, now has the same range for the x and y axes. The figure has been scaled so that the date units of each are represented by the same number of pixels.

You can also adjust your axes to be square, even if the figure is not. Or you can change the default transform for the Circle to None, which means the units used are pixels. I'm having difficulty successfully doing this at the moment (the circle is a circle, but not where I want it to be).

like image 184
Yann Avatar answered Oct 18 '22 18:10

Yann