Question

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.

 3  43  3
1 Jan 1970

Solution

 2

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)
2024-07-10
lastchance

Solution

 1

a complicated scatter plot

Is your idea a very good idea? IMHO a better idea is

  1. use different markers to represent different series,
  2. use a color map to represent the positional order in each one of the series,
  3. use a colorbar to describe the relationship between a marker color and its position in the series.

Here it is the code used to prepare the graph at the top.

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(20240710)
N = 8

cmap = plt.get_cmap('cividis')
markers = 'o s ^ X'.split()
fig = plt.figure(layout='constrained')

for marker in markers:
    plt.scatter(np.random.random(N), np.random.random(N),
                c=cmap(np.arange(N)/(N-1)),
                ec='k', s=144, marker=marker, label=marker)
plt.legend(ncol=4)
cb = plt.colorbar()
cb.set_ticks((0, 1))
cb.set_ticklabels(('First', 'Last'))

plt.show()
2024-07-10
gboffi