I am writing this to have a reference post when it comes to plotting circles with a scatter plot but the user wants to provide a real radius for the scattered circles instead of an abstract size.
I have been looking around and there are other posts that explain the theory of it, but have not come accross a ready-to-use function. I have tried myself and hope that I am close to finding the solution.
This is what I have so far:
import matplotlib.pyplot as plt
import numpy as np
def true_scatter(x, y, r, ax, **kwargs):
# Should work for an equal aspect axis
ax.set_aspect('equal')
# Access the DPI and figure size
dpi = ax.figure.dpi
fig_width_inch, _ = ax.figure.get_size_inches()
# Calculate plot size in data units
xlim = ax.get_xlim()
plot_width_data_units = xlim[1] - xlim[0]
# Calculate the scale factor: pixels per data unit
plot_width_pixels = fig_width_inch * dpi
scale = plot_width_pixels / plot_width_data_units
# Convert radius to pixels, then area to points squared
radius_pixels = r * scale
area_pixels_squared = np.pi * (radius_pixels ** 2)
area_points_squared = area_pixels_squared * (72 / dpi) ** 2
# Scatter plot with converted area
scatter = ax.scatter(x, y, s=area_points_squared, **kwargs)
return scatter
# Example with single scatter
fig, ax = plt.subplots()
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
scatter = true_scatter(0, 0, 1, ax)
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
plt.grid()
plt.show()
Unfortunately, it is not quite the answer. I get a circle of radius ~1.55 instead of 1. Would anyone be able to spot what is wrong with my approach?
Thank you!
It is not very clear whether you want r
to be the radius or the diameter of the circle. The code below supposes it is the radius (just leave out the * 2
if you want the diameter.
For a discussion about how the dot size is measured, see this post.
The code below converts the radius in data units to pixels, and then to "points". Three different sizes are tested.
import matplotlib.pyplot as plt
def true_scatter(x, y, r, ax, **kwargs):
# the radius is given in data coordinates in the x direction
# Should work for an equal aspect axis
ax.set_aspect('equal')
# measure the data coordinates in pixels
radius_in_pixels, _ = ax.transData.transform((r, 0)) - ax.transData.transform((0, 0))
# one "point" is 1/72 of an inch
radius_in_points = radius_in_pixels * 72.0 / ax.figure.dpi
# the scatter plot size is set in square point units
area_points_squared = (radius_in_points * 2) ** 2
# Scatter plot with converted area
scatter = ax.scatter(x, y, s=area_points_squared, **kwargs)
return scatter
# Example with three scatter dots with different radii
fig, ax = plt.subplots(figsize=(5, 5))
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
scatter = true_scatter(0, 0, 1.5, ax)
scatter = true_scatter(0, 0, 1, ax)
scatter = true_scatter(0, 0, 0.5, ax)
ax.grid()
plt.show()