创建一个与散点图大小相关的Matplotlib图例

16

我正在寻找一种方法来包含一个(基于matplotlib库)描述散点图中的点大小的图例,因为这可能与另一个变量有关,就像这个基本示例中的情况:

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()
(inspired from: http://matplotlib.org/examples/shapes_and_collections/scatter_demo.html)
因此,在图例中,理想情况下应该有几个点对应于大小为0-400(变量a2),根据scatter中的s描述符。
6个回答

20

使用.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()

enter image description here


2
这是一个非常有用的解决方案。如果我想要图例中显示的值与实际标记大小不同,我该如何修改呢?例如,如果我想让标记大小从10到100,但表示的值范围是1到1000? - Nathaniel
2
你可以像这样做: labels = ["< 5000", "< 20000", " <50000", "> 50000"] legend = ax.legend(handles, labels, loc="upper right", title="Sizes")``` - AkariYukari
1
我发现如果数据中有任何空值,这个方法就会失败(以防万一其他人无法弄清楚为什么它对他们不起作用)。 - Victor Chubukov

11
下面的解决方案使用了pandas将大小分组为一组 bin(使用groupby)。它绘制每个组并为标记分配一个标签和大小。我使用了这个问题中的分箱食谱。 请注意,这与您所述的问题稍有不同,因为标记大小是分箱的,这意味着a2中的两个元素(例如36和38)将具有相同的大小,因为它们在同一个 bin 中。您始终可以增加箱数以使其更精细,以适合您的需要。
使用此方法,您可以针对每个 bin 变化其他参数,例如标记形状或颜色。
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()

画面


谢谢,这仍然有限制,每个箱子都必须有一个标签,即更多的箱子意味着更多的图例线,除非大多数标签将不包括在图例中。 - gluuke
1
如果您希望绘制一些点而不将它们分配到图例中,则可以分配一个标签“_”,这意味着它们不会添加到图例中。例如,我可以用“_”替换“Small”和“Large”,然后图例将只是["Tiny", "Medium", "Huge"]。 - Ffisegydd

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图表中的点大小图例


这正是我想要的。多点图例有些丑陋,不太适合出版质量。 - ryanjdillon

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)

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)

-1

我在这里找到了链接, 它非常简单易懂。希望可以帮到你。

import matplotlib.pyplot as plt
import numpy as np

import plotly.plotly as py
import plotly.tools as tls

fig = plt.figure()
ax = fig.add_subplot(111)

x = [0,2,4,6,8,10]
y = [0]*len(x)
s = [100,  400, 490, 600, 240, 160] # Specifies marker size

ax.scatter(x,y,s=s)
ax.set_title('Plot with Different Marker size, matplotlib and plotly')

plotly_fig = tls.mpl_to_plotly( fig )
plotly_fig['layout']['showlegend'] = True
plotly_url = py.plot(plotly_fig, filename='mpl-marker-size')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接