创建与大小相关的matplotlib散点图图例

16 投票
6 回答
29138 浏览
提问于 2025-04-18 09:25

我想找一种方法,在散点图中添加一个图例,用来说明点的大小,因为这可能和另一个变量有关,就像这个简单的例子一样:

import numpy as np
import matplotlib.pyplot as plt

N = 50
x = np.random.rand(N)
y = np.random.rand(N)
a2 = 400*np.random.rand(N)

plt.scatter(x, y, s=a2, alpha=0.5)
plt.show()

(灵感来源于: http://matplotlib.org/examples/shapes_and_collections/scatter_demo.html

所以在图例中,理想情况下会有几个点,分别对应于大小0到400(这个a2变量),根据scatter中的s描述符来显示。

6 个回答

1

在mjp和jpobst的回答基础上,如果你有超过两种不同的大小,可以使用循环来把标签包含在调用plt.scatter()的时候:

msizes = [3, 4, 5, 6, 7]
markers = []
for size in msizes:
   markers.append(plt.scatter([],[], s=size, label=size))

plt.legend(handles=markers)

注意,你可以使用标准的字符串格式化来设置标签,比如在mjp的回答中,标签可以写成label = ('M%d' %size)

5

我差不多喜欢mjp的回答,但它不太对,因为plt.plot中的'markersize'参数和plt.scatter中的's'参数意思不一样。使用plt.plot的话,你的大小会不正确。

所以应该使用:

    marker1 = plt.scatter([],[], s=a2.min())
    marker2 = plt.scatter([],[], s=a2.max())
    legend_markers = [marker1, marker2]

    labels = [
        str(round(a2.min(),2)),
        str(round(a2.max(),2))
        ]

    fig.legend(handles=legend_markers, labels=labels, loc='upper_right',
        scatterpoints=1)
6

这个方法也可以用,我觉得它稍微简单一点:

msizes = np.array([3, 4, 5, 6, 7, 8])

l1, = plt.plot([],[], 'or', markersize=msizes[0])
l2, = plt.plot([],[], 'or', markersize=msizes[1])
l3, = plt.plot([],[], 'or', markersize=msizes[2])
l4, = plt.plot([],[], 'or', markersize=msizes[3])

labels = ['M3', 'M4', 'M5', 'M6']

leg = plt.legend([l1, l2, l3, l4], labels, ncol=1, frameon=True, fontsize=12,
handlelength=2, loc = 8, borderpad = 1.8,
handletextpad=1, title='My Title', scatterpoints = 1)

摘自:matplotlib和basemap图中的点大小图例

21

使用 .legend_elements("sizes")

import numpy as np
import matplotlib.pyplot as plt

N = 50
x = np.random.rand(N)
y = np.random.rand(N)
a2 = 400*np.random.rand(N)

sc = plt.scatter(x, y, s=a2, alpha=0.5)
plt.legend(*sc.legend_elements("sizes", num=6))
plt.show()

在这里输入图片描述

11

下面的解决方案使用了 pandas 这个工具,将不同的大小分组到几个固定的范围里(这就是 groupby 的作用)。它会把每个分组画出来,并给每个分组分配一个标签和一个标记的大小。我参考了 这个问题 中的分组方法。

注意,这和你提到的问题有点不同,因为标记的大小是分组的,这意味着在 a2 中的两个元素,比如 36 和 38,会有相同的大小,因为它们在同一个分组里。你可以增加分组的数量,让它们更细致,按你的需要来调整。

使用这种方法,你还可以为每个分组改变其他参数,比如标记的形状或颜色。

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

N = 50
M = 5 # Number of bins

x = np.random.rand(N)
y = np.random.rand(N)
a2 = 400*np.random.rand(N)

# Create the DataFrame from your randomised data and bin it using groupby.
df = pd.DataFrame(data=dict(x=x, y=y, a2=a2))
bins = np.linspace(df.a2.min(), df.a2.max(), M)
grouped = df.groupby(np.digitize(df.a2, bins))

# Create some sizes and some labels.
sizes = [50*(i+1.) for i in range(M)]
labels = ['Tiny', 'Small', 'Medium', 'Large', 'Huge']

for i, (name, group) in enumerate(grouped):
    plt.scatter(group.x, group.y, s=sizes[i], alpha=0.5, label=labels[i])

plt.legend()
plt.show()

Plot

撰写回答