Search code examples
pythonmatplotlibscatter-plot

Correct Sizing of Markers In Scatter Plot to a Radius R in Matplotlib


I have a plot with limits -1 to 1. I know the scatter plot doesn't plot with size as a radius, it plots with size as a point.

I need my plot to be scaled correctly with the size of each point, which I have as a radius. Is this possible with modifications to the below code?

fig, ax = plt.subplots(1)
ax.set_title("Post Synaptic Neurons")
sizes = [x.size * 100 for x in post_synaptic_neurons]
offsets = [(x.origin[0],x.origin[1]) for x in post_synaptic_neurons]
print(sizes)
ax.scatter([x.origin[0] for x in post_synaptic_neurons], [x.origin[1] for x in post_synaptic_neurons], 
  cmap=plt.cm.hsv, s=sizes, alpha=0.5)
ax.set_xlim([-1,1]) 
ax.set_ylim([-1,1])
ax.set_aspect(1)
plt.tight_layout

If no, can someone explain to me why matplotlib doesn't have a function for plotting a circle with a particular radius on the scale of the plot? I didn't expect this to be an issue, but there must be a good reason behind my difficulties.


Solution

  • As far as I know there is not a high-level way to do this, but you can make it work with an EllipseCollection. Since your data isn't available, I made some up and wrote this code:

    import matplotlib.pyplot as plt
    import numpy as np
    from matplotlib.collections import EllipseCollection
    
    x, y = 1500 * np.random.rand(2, 100)
    size = 50 + 10 * np.random.randn(100)
    color = np.random.rand(100)
    cmap = plt.cm.hsv
    
    fig, ax = plt.subplots(1, 2, figsize=(16, 6),
                           sharex=True, sharey=True)
    ax[0].scatter(x, y, s=size, c=color, cmap=cmap)
    
    offsets = list(zip(x, y))
    ax[1].add_collection(EllipseCollection(widths=size, heights=size, angles=0, units='xy',
                                           facecolors=plt.cm.hsv(color),
                                           offsets=offsets, transOffset=ax[1].transData))
    ax[1].axis('equal') # set aspect ratio to equal
    ax[1].axis([-400, 1800, -200, 1600])
    

    The result is what you hope for; simple scatter plot on the left, scaled plot on the right: enter image description here

    If you scale the plots, say by changing the axis limits to ax[1].axis([200, 1000, 300, 900]), then the circles on the right plot scale as desired:

    enter image description here

    You can find more examples of using collections in the matplotlib docs.