pyplot 散点图标记大小
在pyplot的散点图文档中:
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)
标记的大小
s:单位是平方点。它可以是一个数值,也可以是一个和x、y长度相同的数组。
那么“平方点”是什么单位呢?这是什么意思?如果 s=100 ,是不是意味着 10像素 x 10像素 ?
我主要是想制作不同标记大小的散点图,想弄清楚这个 s 的数字到底代表什么。
7 个回答
这是关于标记的面积。我的意思是,如果你有 s1 = 1000
和 s2 = 4000
,那么这两个圆的半径之间的关系是:r_s2 = 2 * r_s1
。看看下面的图:
plt.scatter(2, 1, s=4000, c='r')
plt.scatter(2, 1, s=1000 ,c='b')
plt.scatter(2, 1, s=10, c='g')
我在看到这个帖子时也有同样的疑问,所以我做了这个例子,然后在屏幕上用尺子测量了半径。
因为其他回答说 s
代表标记的面积,所以我来澄清一下,这并不一定是对的。
大小以点为单位
在 plt.scatter
中,s
参数表示的是 markersize**2
。就像文档中说的那样:
s
: 标量或类似数组,形状为 (n, ),可选
大小以点的平方为单位。默认值是 rcParams['lines.markersize'] ** 2。
这可以字面理解。为了得到一个大小为 x 点的标记,你需要把这个数字平方后传给 s
参数。
所以,线图的标记大小和散点图的大小参数之间的关系是平方关系。为了让散点图的标记和大小为 10 点的线图标记一样大,你需要调用 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()
与“面积”的关系
那么,为什么其他回答甚至文档在谈到 s
参数时会提到“面积”呢?
当然,点的平方是面积单位。
- 对于特殊情况的方形标记,
marker="s"
,标记的面积确实直接等于s
参数的值。 - 对于圆形,圆的面积是
area = pi/4*s
。 - 对于其他标记,可能与标记的面积没有明显关系。
不过,在所有情况下,标记的面积与 s
参数是成比例的。这就是为什么称其为“面积”,即使在大多数情况下并不完全如此。
用与标记面积成比例的某个量来指定散点标记的大小是有道理的,因为在比较不同的标记时,感知到的是标记的面积,而不是边长或直径。也就是说,基础量翻倍应该使标记的面积翻倍。
什么是点?
到目前为止,散点标记的大小是以点为单位的。点通常用于排版,字体的大小也是用点来表示的。线宽也常常用点来表示。在 matplotlib 中,标准的点大小是每英寸 72 点(ppi)——因此 1 点等于 1/72 英寸。
能够用像素而不是点来指定大小可能会很有用。如果图形的 dpi 也是 72,那么 1 点就是 1 像素。如果图形的 dpi 不同(matplotlib 的默认值是 fig.dpi=100
),
1 point == fig.dpi/72. pixels
因此,散点标记的大小在点数上会因图形 dpi 的不同而有所不同,但可以制作一个 10 x 10 像素的标记,这样无论如何都会覆盖相同数量的像素:
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()
如果你对数据单位的散点图感兴趣,可以查看 这个回答。
这种定义大小的方式可能有点让人困惑,但其实你是在指定标记的面积。这意味着,如果你想把标记的宽度(或高度)加倍,你需要把s
增加4倍。[因为面积A = 宽W × 高H,所以(2W)(2H)=4A]
不过,标记的大小是这样定义是有原因的。因为面积是宽度的平方,所以当你把宽度加倍时,实际看起来大小会比加倍还要大(实际上是增加了4倍)。为了更好理解,可以看看下面两个例子及其输出。
# 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()
结果是
注意到大小增加得非常快。如果我们改成
# 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()
结果是
现在标记的明显大小增加得比较线性,符合直觉。
至于“点”的确切含义,对于绘图来说是相对随意的,你可以把所有的大小都按一个常数放大,直到看起来合适为止。
编辑:(回应@Emma的评论)
可能是我用词不够清晰。问题是关于把一个圆的宽度加倍,所以在第一张图中,每个圆(从左到右)宽度都是前一个的两倍,这样面积就是以4为底的指数增长。同样,第二个例子中每个圆的面积是前一个的两倍,这样就是以2为底的指数增长。
不过,第二个例子(我们在放大面积)中,面积加倍看起来圆的大小也加倍。因此,如果我们想让一个圆看起来大n
倍,我们应该把面积增加n
倍,而不是半径,这样明显的大小就会和面积成线性关系。
编辑 为了更好地理解@TomaszGandor的评论:
这是不同标记大小函数的效果:
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()