pythonmatplotlib

How to have the size of markers match in a matplotlib plot and in its legend?


I have different (X,Y) data series drawn as scatter plots on one figure. Within each series, the marker size is set to different values according to the rank of the data point within the series (i.e. the 1st point in the series is shown with a smaller marker than the 2nd, which itself is shown with a smaller marker than the 3rd and so on…).

I want to show in the legend which marker size correspond to which rank. I managed to set the marker size in the legend handles to the same values as those on the graph. I also set the markerscale keyword of the legend() function to 1, aiming to have the markers be the same size in both legend and graph.

However, the markers appear significantly larger in the legend than in the graph. Here is a code snippet replicating my issue.

import numpy as np
import numpy.random as rd
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D

rd.seed(12345)

fig,ax = plt.subplots()

# Number of time steps in each series
nb_steps = 5
t = np.linspace(1,nb_steps,nb_steps)

# Series 1
x1 = rd.random(nb_steps)
y1 = rd.random(nb_steps)

# Series 2
x2 = 1+2*rd.random(nb_steps)
y2 = 1+rd.random(nb_steps)

# Plotting with increasing marker size within series
marker_sizes = (t+3)**2
ax.scatter(x1,y1, marker = '+', s = marker_sizes)
ax.scatter(x2,y2, marker = '+', s = marker_sizes)

# Writing marker size legend
handles = []
for size, step in zip(marker_sizes, t):
    handles.append(Line2D([0], [0], marker='+', lw = 0, color='k', markersize=size, label=step))

ax.legend(handles = handles, markerscale=1)
plt.show()

And here is the output figure

Thanks in advance for your help.


Solution

  • The markersizes in pyplot.scatter use the squared form, proportional to area (as you have already noted). However, those in the legend appear not to.

    I suggest that you set your marker sizes as the linear form (so that they are OK for the legend):

    marker_sizes = t+3
    

    and then make the squaring explicit in pyplot.scatter() (but NOT in the legend):

    ax.scatter(x1,y1, marker = '+', s = marker_sizes**2)