623

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.

cottontail
  • 10,268
  • 18
  • 50
  • 51
LWZ
  • 11,670
  • 22
  • 61
  • 79
  • 2
    pretty sure that points are the same units used for fonts. – tacaswell Feb 12 '13 at 15:19
  • 4
    @tcaswell, you mean `s=20` means the marker size equals that of a `fontsize=20` letter? – LWZ Feb 12 '13 at 19:19
  • no, the area will be 20 points^2, a `fontsize=20` letter is 20 pts tall (or what ever the reference character in the font is is 20 pts tall). – tacaswell Feb 12 '13 at 19:24
  • 60
    ```matplotlib.pyplot.plot()``` has ``ms`` parameter (``markersize``) an equivalent for ``matplotlib.pyplot.scatter()`` parameter ``s`` (``size``). Just a reminder.. – niekas Nov 06 '14 at 10:08
  • 1
    @neikas it seems to me they are not, since one is in pixels (markersize) and other is in this weird squared points unit (size). This has always been confusing to me, but I believe it has to do with scatterplot marker size being used to denote amount in a visually proportional way. – heltonbiker May 26 '17 at 18:02
  • 4
    @heltonbiker is right on this one. If you want to match the `markersize` from the `plot` function to `s` from the `scatter` function, you need to square it, i.e. `s = markersize**2`. – Marses Sep 20 '20 at 09:01
  • As far as I can tell, with `matplotlib.pyplot.scatter` the argument `s` stands for "size" but `size` is not a valid argument (`'PathCollection' object has no property 'size'`). Meanwhile both `c` and `color` are valid arguments. – PatrickT Nov 22 '21 at 10:49

7 Answers7

664

This can be a somewhat confusing way of defining the size but you are basically specifying the area of the marker. This means, to double the width (or height) of the marker you need to increase s by a factor of 4. [because A = WH => (2W)(2H)=4A]

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()
starball
  • 20,030
  • 7
  • 43
  • 238
Dan
  • 12,857
  • 7
  • 40
  • 57
  • 3
    I'm probably misunderstanding your point, but in your second example you are increasing s exponentially (s=[20, 40, 80, 160, 320, 640]) and saying that that gives us a nice linear-looking size increase. Wouldn't it make more sense if increasing the size linearly (ex. s=[20, 40, 60, 80, 100, 120]) gave us the linear-looking result? – Emma Oct 22 '13 at 20:20
  • @Emma Your intuition is right, it's poor wording on my part (alternatively poor choice of x axis scaling). I explained some more in an edit because it was too long for a comment. – Dan Oct 22 '13 at 22:00
  • 1
    Is it possible to change `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. – Sigur May 10 '17 at 17:35
  • 3
    Great example (just the necessary stuff!). This should not be `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). – Tomasz Gandor Jun 13 '19 at 11:35
  • 1
    To put it shorter - the second plot shows square root of exponential - which is another exponential, just a bit less steep. – Tomasz Gandor Jun 13 '19 at 11:39
  • Anyone knows why, only `s` works but not `markersize` as stated in the documentation? – aerijman Sep 19 '19 at 17:55
  • Nice. How do you get a marker with a black outline and a blue fill? (using `plt.scatter`) – PatrickT Nov 22 '21 at 10:52
  • @aerijman See https://stackoverflow.com/a/47403507/365102 – Mateen Ulhaq Jun 19 '23 at 23:55
354

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).

enter image description here

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 the s parameter.
  • 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.

enter image description here

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.

enter image description here

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.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Wondering how would one calculate what s parameter to give to scatter to get a circle which covers diameter of, let's say, 0.1 in real coordinates of the plot (so as to fill the gap between let's say 0.4 and 0.5 on a plot from (0,0) to (1,1)? – Anatoly Alekseev Dec 26 '18 at 19:56
  • @AnatolyAlekseev That should be answered by [this](https://stackoverflow.com/a/48174228/4124317) question. – ImportanceOfBeingErnest Feb 17 '19 at 04:28
  • @ImportanceOfBeingErnest could you pls explain how to get the radius of a scatter based on the `s` param passed? I thought it is `np.sqrt(s)/2`. https://stackoverflow.com/q/64399664/9900084 – steven Oct 17 '20 at 15:46
35

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.

jmd_dk
  • 12,125
  • 9
  • 63
  • 94
Joaquin
  • 451
  • 4
  • 3
35

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

enter image description here

zhaoqing
  • 775
  • 8
  • 8
  • 2
    The question was about scatterplot, and in matplotlib the two plotting functions have different parameters (**markersize** for _plot_, and **s** for _scatter_). So this answer doesn't apply. – Dom Apr 28 '17 at 09:19
  • 3
    @Dom I upvoted, because this question pops up as the first result in google even when I search "pyplot plot marker size", so this answer helps. – Przemek D Jul 26 '17 at 08:18
  • I know the plot method and the scatter method are different in plt but they both can realize the 'scatter plot' and adjust markersize, so this answer is just another working around if you use plot method @Dom – zhaoqing Jul 28 '17 at 05:42
8

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)

enter image description here

This is based on an answer to this question

Ike
  • 1,039
  • 11
  • 10
2

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]

DomTomCat
  • 8,189
  • 1
  • 49
  • 64
user34028
  • 143
  • 7
0

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);

scatter

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)}");

scatter plot comparison

cottontail
  • 10,268
  • 18
  • 50
  • 51