In the pyplot document for scatter plot:
matplotlib.pyplot.scatter(x, y, s=20, c='b', marker='o', cmap=None, norm=None,
vmin=None, vmax=None, alpha=None, linewidths=None,
faceted=True, verts=None, hold=None, **kwargs)
The marker size
s: size in points^2. It is a scalar or an array of the same length as x and y.
What kind of unit is points^2? What does it mean? Does s=100 mean 10 pixel x 10 pixel?
Basically I'm trying to make scatter plots with different marker sizes, and I want to figure out what does the s number mean.
7 Answers 7
s is an area
s is an area (measured in pt2), and is the square of a length (measured in pt):
s = area = length**2
For example, these are all proportional to length**2:
square_area = w**2 --> 1 * length**2circle_area = π * r**2 --> π/4 * length**2
To double the width and height of any marker, multiply s by a factor of 4:
A = W * H --> (2W) * (2H) = 4A
(Warning: s is proportional to the marker's shaded area, but is usually not equal to it.)
Why is it like this?
There is a reason, however, that the size of markers is defined in this way. Because of the scaling of area as the square of width, doubling the width actually appears to increase the size by more than a factor 2 (in fact it increases it by a factor of 4). To see this consider the following two examples and the output they produce.
# doubling the width of markers
x = [0,2,4,6,8,10]
y = [0]*len(x)
s = [20*4**n for n in range(len(x))]
plt.scatter(x,y,s=s)
plt.show()
gives
enter image description here
Notice how the size increases very quickly. If instead we have
# doubling the area of markers
x = [0,2,4,6,8,10]
y = [0]*len(x)
s = [20*2**n for n in range(len(x))]
plt.scatter(x,y,s=s)
plt.show()
gives
enter image description here
Now the apparent size of the markers increases roughly linearly in an intuitive fashion.
As for the exact meaning of what a 'point' is, it is fairly arbitrary for plotting purposes, you can just scale all of your sizes by a constant until they look reasonable.
Edit: (In response to comment from @Emma)
It's probably confusing wording on my part. The question asked about doubling the width of a circle so in the first picture for each circle (as we move from left to right) it's width is double the previous one so for the area this is an exponential with base 4. Similarly the second example each circle has area double the last one which gives an exponential with base 2.
However it is the second example (where we are scaling area) that doubling area appears to make the circle twice as big to the eye. Thus if we want a circle to appear a factor of n bigger we would increase the area by a factor n not the radius so the apparent size scales linearly with the area.
Edit to visualize the comment by @TomaszGandor:
This is what it looks like for different functions of the marker size:
Exponential, Square, or Linear size
x = [0,2,4,6,8,10,12,14,16,18]
s_exp = [20*2**n for n in range(len(x))]
s_square = [20*n**2 for n in range(len(x))]
s_linear = [20*n for n in range(len(x))]
plt.scatter(x,[1]*len(x),s=s_exp, label='$s=2^n$', lw=1)
plt.scatter(x,[0]*len(x),s=s_square, label='$s=n^2$')
plt.scatter(x,[-1]*len(x),s=s_linear, label='$s=n$')
plt.ylim(-1.5,1.5)
plt.legend(loc='center left', bbox_to_anchor=(1.1, 0.5), labelspacing=3)
plt.show()
7 Comments
s value according to the size of figure window? I mean, if we maximize the figure windows, I'd like to have bigger size marks.4 ** n and 2 ** n, but n ** 4 and n ** 2. With 2 ** n the second plot does not scale linearly in terms of circle diameter. It still goes too fast (just not that much over the top).Because other answers here claim that s denotes the area of the marker, I'm adding this answer to clearify that this is not necessarily the case.
Size in points^2
The argument s in plt.scatter denotes the markersize**2. As the documentation says
s: scalar or array_like, shape (n, ), optional
size in points^2. Default is rcParams['lines.markersize'] ** 2.
This can be taken literally. In order to obtain a marker which is x points large, you need to square that number and give it to the s argument.
So the relationship between the markersize of a line plot and the scatter size argument is the square. In order to produce a scatter marker of the same size as a plot marker of size 10 points you would hence call scatter( .., s=100).
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
ax.plot([0],[0], marker="o", markersize=10)
ax.plot([0.07,0.93],[0,0], linewidth=10)
ax.scatter([1],[0], s=100)
ax.plot([0],[1], marker="o", markersize=22)
ax.plot([0.14,0.86],[1,1], linewidth=22)
ax.scatter([1],[1], s=22**2)
plt.show()
Connection to "area"
So why do other answers and even the documentation speak about "area" when it comes to the s parameter?
Of course the units of points**2 are area units.
- For the special case of a square marker,
marker="s", the area of the marker is indeed directly the value of thesparameter. - For a circle, the area of the circle is
area = pi/4*s. - For other markers there may not even be any obvious relation to the area of the marker.
In all cases however the area of the marker is proportional to the s parameter. This is the motivation to call it "area" even though in most cases it isn't really.
Specifying the size of the scatter markers in terms of some quantity which is proportional to the area of the marker makes in thus far sense as it is the area of the marker that is perceived when comparing different patches rather than its side length or diameter. I.e. doubling the underlying quantity should double the area of the marker.
What are points?
So far the answer to what the size of a scatter marker means is given in units of points. Points are often used in typography, where fonts are specified in points. Also linewidths is often specified in points. The standard size of points in matplotlib is 72 points per inch (ppi) - 1 point is hence 1/72 inches.
It might be useful to be able to specify sizes in pixels instead of points. If the figure dpi is 72 as well, one point is one pixel. If the figure dpi is different (matplotlib default is fig.dpi=100),
1 point == fig.dpi/72. pixels
While the scatter marker's size in points would hence look different for different figure dpi, one could produce a 10 by 10 pixels^2 marker, which would always have the same number of pixels covered:
enter image description here enter image description here enter image description here
import matplotlib.pyplot as plt
for dpi in [72,100,144]:
fig,ax = plt.subplots(figsize=(1.5,2), dpi=dpi)
ax.set_title("fig.dpi={}".format(dpi))
ax.set_ylim(-3,3)
ax.set_xlim(-2,2)
ax.scatter([0],[1], s=10**2,
marker="s", linewidth=0, label="100 points^2")
ax.scatter([1],[1], s=(10*72./fig.dpi)**2,
marker="s", linewidth=0, label="100 pixels^2")
ax.legend(loc=8,framealpha=1, fontsize=8)
fig.savefig("fig{}.png".format(dpi), bbox_inches="tight")
plt.show()
If you are interested in a scatter in data units, check this answer.
3 Comments
s param passed? I thought it is np.sqrt(s)/2. stackoverflow.com/q/64399664/9900084 It is the area of the marker. I mean if you have s1 = 1000 and then s2 = 4000, the relation between the radius of each circle is: r_s2 = 2 * r_s1. See the following plot:
plt.scatter(2, 1, s=4000, c='r')
plt.scatter(2, 1, s=1000 ,c='b')
plt.scatter(2, 1, s=10, c='g')
enter image description here
I had the same doubt when I saw the post, so I did this example then I used a ruler on the screen to measure the radii.
1 Comment
You can use markersize to specify the size of the circle in plot method
import numpy as np
import matplotlib.pyplot as plt
x1 = np.random.randn(20)
x2 = np.random.randn(20)
plt.figure(1)
# you can specify the marker size two ways directly:
plt.plot(x1, 'bo', markersize=20) # blue circle with size 10
plt.plot(x2, 'ro', ms=10,) # ms is just an alias for markersize
plt.show()
From here
4 Comments
I also attempted to use 'scatter' initially for this purpose. After quite a bit of wasted time - I settled on the following solution.
import matplotlib.pyplot as plt
input_list = [{'x':100,'y':200,'radius':50, 'color':(0.1,0.2,0.3)}]
output_list = []
for point in input_list:
output_list.append(plt.Circle((point['x'], point['y']), point['radius'], color=point['color'], fill=False))
ax = plt.gca(aspect='equal')
ax.cla()
ax.set_xlim((0, 1000))
ax.set_ylim((0, 1000))
for circle in output_list:
ax.add_artist(circle)
This is based on an answer to this question
Since this is the top search engine result for "how to change scatter plot marker sizes in python", here is a summary of scatter plot marker size definitions in some of the most popular plotting libraries in Python:
- matplotlib (where
s=markersize**2):plt.scatter(x, y, s=9)plt.plot(x, y, 'o', markersize=3)
- pandas:
df.plot(x='A', y='B', kind='scatter', s=9)df.plot(x='A', y='B', marker='o', linestyle='', markersize=3)
- seaborn:
sns.scatterplot(x=x, y=y, s=9)
On the subject of scatter plot marker size, one important thing not mentioned here is that each marker defines edge color and width and depending on how these are set, the resulting markers may end up with different sizes.
By default, an edge with width=1 (lw=1) point is drawn around the markers, which makes the markers bigger. As you can see from the following code, if we remove the edge lines (by lw=0), we get a much smaller marker. In fact, s=1 and lw=1 draws a marker the same size as s=4 and lw=0.
fig, ax = plt.subplots(figsize=(6/72, 2/72), dpi=72*50)
ax.scatter([-0.3], [0], s=1, lw=0)
ax.scatter([1.5], [0], s=1)
ax.scatter([3.8], [0], s=4, lw=0)
ax.set(position=[0,0,1,1], xticks=[], yticks=[], xlim=(-1,5))
plt.setp(ax.spines.values(), linewidth=0.01);
For a more concrete example, in the following figure, the same marker size (s=36) was passed to seaborn, matplotlib and pandas scatter-plot plotters but because the default edge color and marker edge width in seaborn are different from those in the other two methods, the scatter plots end up with markers of different sizes.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
x = range(20)
df = pd.DataFrame({'A': x, 'B': x})
fig, axs = plt.subplots(1, 3, figsize=(15,3), facecolor='white')
sns.scatterplot(x=x, y=x, ax=axs[0], s=36);
axs[1].scatter(x, x, s=36);
df.plot.scatter('A', 'B', ax=axs[2], s=36);
ms = {f"ax{i}": ax.collections[0] for i, ax in enumerate(axs)}
axs[0].set_title(f"seaborn scatterplot\nmarker edgewidth: {ms['ax0'].get_lw()[0]}\nedgecolor: {ms['ax0'].get_ec()[0]}")
axs[1].set_title(f"matplotlib scatter\nmarker edgewidth: {ms['ax1'].get_lw()[0]}\nedgecolor: {ms['ax1'].get_ec()[0].round(2)}");
axs[2].set_title(f"pandas scatter plot\nmarker edgewidth: {ms['ax2'].get_lw()[0]}\nedgecolor: {ms['ax2'].get_ec()[0].round(2)}");
Comments
If the size of the circles corresponds to the square of the parameter in s=parameter, then assign a square root to each element you append to your size array, like this: s=[1, 1.414, 1.73, 2.0, 2.24] such that when it takes these values and returns them, their relative size increase will be the square root of the squared progression, which returns a linear progression.
If I were to square each one as it gets output to the plot: output=[1, 2, 3, 4, 5]. Try list interpretation: s=[numpy.sqrt(i) for i in s]
2 Comments
i in output shouldn't?Explore related questions
See similar questions with these tags.
s=20means the marker size equals that of afontsize=20letter?matplotlib.pyplot.plot()hasmsparameter (markersize) an equivalent formatplotlib.pyplot.scatter()parameters(size). Just a reminder..markersizefrom theplotfunction tosfrom thescatterfunction, you need to square it, i.e.s = markersize**2.