如何绘制带有每个类别图例标签的散点图。

9

我对在散点图中绘制图例很感兴趣。我的当前代码如下:

x=[1,2,3,4]
y=[5,6,7,8]
classes = [2,4,4,2]
plt.scatter(x, y, c=classes, label=classes)
plt.legend()

问题在于当绘制图表时,图例显示为数组,而不是显示唯一标签及其类别。

This is how the plot looks

我知道这个问题在之前的讨论中已经被提出,例如这个one,但是我觉得我的问题更简单,那里的解决方案并不适用。此外,在那个例子中,人们正在指定颜色,然而在我的情况下,我事先知道需要多少种颜色。此外,在this的例子中,用户正在创建多个散点图,每个散点图都有一个独特的颜色。再次强调,这不是我想要的。我的目标只是使用x, y数组和标签创建绘图。这可能吗?

3个回答

13

实际上,这两个相关的问题都提供了如何实现所需结果的方法。

最简单的方法是创建与唯一类别相同数量的散点图,并为每个散点图分配单个颜色和图例条目。

import matplotlib.pyplot as plt

x=[1,2,3,4]
y=[5,6,7,8]
classes = [2,4,4,2]
unique = list(set(classes))
colors = [plt.cm.jet(float(i)/max(unique)) for i in unique]
for i, u in enumerate(unique):
    xi = [x[j] for j  in range(len(x)) if classes[j] == u]
    yi = [y[j] for j  in range(len(x)) if classes[j] == u]
    plt.scatter(xi, yi, c=colors[i], label=str(u))
plt.legend()

plt.show()

enter image description here

如果类是字符串标签,解决方案将略有不同,需要从它们的索引中获取颜色而不是使用类本身。

import numpy as np
import matplotlib.pyplot as plt

x=[1,2,3,4]
y=[5,6,7,8]
classes = ['X','Y','Z','X']
unique = np.unique(classes)
colors = [plt.cm.jet(i/float(len(unique)-1)) for i in range(len(unique))]
for i, u in enumerate(unique):
    xi = [x[j] for j  in range(len(x)) if classes[j] == u]
    yi = [y[j] for j  in range(len(x)) if classes[j] == u]
    plt.scatter(xi, yi, c=colors[i], label=str(u))
plt.legend()

plt.show()

输入图像描述


13
我是唯一一个觉得没有内置方法来完成这个任务很惊讶的人吗?我觉得绘制不同类别点的分布是一个非常常见的任务。如果您知道原因,请告诉我。 - Johannes
6
为散点图建立图例,仅显示哪种颜色对应哪个标签。我觉得循环标签很令人惊讶。(在我看来)非常频繁的用例是在机器学习中:对特征进行高级降维,并希望将它们绘制在二维平面上。您会得到属于有限类别(->您的标签/颜色)的大量点。例如,搜索“t-SNE图”。 - Johannes
1
@Johannes,您无需循环处理类。例如,这里的另一个答案根本没有循环。我不知道在使用matplotlib创建的所有图中,有多少比例属于机器学习范畴,但是Matplotlib肯定不专门针对任何领域;它只是提供了创建数据图的手段。它的核心代码应该比机器学习炒作还要老。在matplotlib中创建自定义图例非常容易,因此如果您需要反复执行该操作,可以编写自己的scatterlegend(sc, values, classes = None)函数。如果您需要帮助,请为什么不提出一个新问题呢? - ImportanceOfBeingErnest
@ImportanceOfBeingErnest,如果“classes”是字符串,那怎么办?将“float”更改为“str”将会产生“TypeError”。 - user9639519
@ImportanceOfBeingErnest 当然,这只是一个建议。 - user9639519
显示剩余6条评论

5
也许手动填写一个 table 在这里会很有用。另一个想法是如果你的类是连续的数字,可以使用 colorbar。我将两种方法结合在了一起。
import matplotlib.pyplot as plt
import numpy as np

x=[1,2,3,4,5,6,7]
y=[1,2,3,4,5,6,7]
classes = [2,4,4,2,1,3,5]
cmap = plt.cm.get_cmap("viridis",5)
plt.scatter(x, y, c=classes, label=classes,cmap=cmap,vmin=0.5,vmax=5.5)
plt.colorbar()
unique_classes = list(set(classes))
plt.table(cellText=[[x] for x in unique_classes], loc='lower right',
          colWidths=[0.2],rowColours=cmap(np.array(unique_classes)-1),
         rowLabels=['label%d'%x for x in unique_classes],
          colLabels=['classes'])

enter image description here


1
好主意!只是颜色映射有点巧妙。 - ImportanceOfBeingErnest
1
是的,它需要一些微调,并且仅适用于您的数据在区间内都是数字。 - Pablo Reyes

0
最简单的解决方案是使用seaborn,这是一个用于matplotlib的高级API,它通过使用hue参数来将组分开并以不同颜色显示。 legend='full':确保每个组在图例中都有条目,这在hue类别为数值时非常重要。
如果使用Anaconda发行版,则seaborn已经安装在(base)环境中。否则,请使用pip在非Anaconda环境中进行安装。
import seaborn as sns
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6.5, 3.5))

sns.scatterplot(x=x, y=y, hue=classes, legend='full', ax=ax)

enter image description here

g = sns.relplot(kind='scatter', x=x, y=y, hue=classes, legend='full', height=3.5, aspect=1.5)

enter image description here


选择性地,从数据列表创建一个pandas.DataFrame,然后使用seaborn进行绘图。
import pandas as pd

# create the dataframe
df = pd.DataFrame({'x': x, 'y': y, 'classes': classes})

# axes level plot
ax = sns.scatterplot(data=df, x='x', y='y', hue='classes', legend='full')

# figure level plot
g = sns.relplot(kind='scatter', data=df, x='x', y='y', hue='classes', legend='full')

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