【问题标题】:Add legend to a matplotlib scatter plot where colors are dynamic将图例添加到颜色是动态的 matplotlib 散点图中
【发布时间】:2020-06-24 09:13:12
【问题描述】:

我使用 Matplolib 和 Pandas 数据框创建了一个散点图,现在我想为其添加一个图例。这是我的代码:

colors = ['red' if x >= 150 and x < 200 else 
          'green' if x >= 200 and x < 400 else
          'purple' if x >= 400 and x < 600 else
          'yellow' if x >= 600 else 'teal' for x in myData.R]


ax1.scatter(myData.X, myData.Y, s=20, c=colors, marker='_', label='Test')
ax1.legend(loc='upper left', frameon=False)

这里发生的是,根据myData.R的值,散点图中点的颜色会发生变化。所以,由于颜色是“动态的”,我在创建图例时遇到了很多麻烦。实际代码只会创建一个带有一个名为“Test”的标签的图例,附近没有任何颜色。

以下是数据示例:

       X  Y    R
0      1  945  1236.334519
0      1  950   212.809352
0      1  950   290.663847
0      1  961   158.156856

我试过this,但我不明白的是:

  1. 如何动态为图例设置标签?例如,我的代码是'red' if x &gt;= 150,所以在图例上应该有一个红色方块,旁边有>150。但由于我没有手动添加任何标签,我很难理解这一点。

  2. 尝试以下方法后,我只得到了一个带有单个标签“类”的图例:

`legend1 = ax1.legend(*scatter.legend_elements(), loc="左下", title="类")

ax1.add_artist(legend1)`

欢迎任何建议!

【问题讨论】:

  • 不确定,但它在docs 中说散点图应该有一个cmapvminvmax 参数。他们不解决你的问题吗?除了图例,您还可以使用自定义颜色条..
  • 另一个想法是根据myData.R预先将myData.XmyData.Y拆分为特定的彩色数据集,并将每个子集作为额外的散点图添加到您的图形中,并使用固定的颜色和标签.
  • @RolandDeschain 所以我们的想法是为所有红色元素绘制散点图,为绿色元素绘制另一个散点图,等等?不过,生成不是需要更多时间吗?
  • 取决于你有多少数据点。绘图本身不应该是问题,分离到子集可能是最耗时的,但如果你可以用 numpy.这是为了某种动画吗?
  • Here 显示了我用彩条表示的意思(在答案中)。您可以创建自定义范围并绘制完整的数据集,然后根据您在颜色栏中的设置绘制颜色。散点图也支持这些。

标签: python python-3.x matplotlib


【解决方案1】:

可以加速的部分代码是使用纯 Python 循环创建字符串列表。 Pandas 使用 numpy 的过滤非常有效。 绘制散点图主要取决于点的数量,一次绘制所有点或分五部分绘制时,点数不会改变。

循环使用matplotlib的scatter的一些示例代码:

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

N = 500
myData = pd.DataFrame({'X': np.round(np.random.uniform(-1000, 1000, N), -2), 
                       'Y': np.random.uniform(-800, 800, N)})
myData['R'] = np.sqrt(myData.X ** 2 + myData.Y ** 2)

fig, ax1 = plt.subplots()

bounds = [150, 200, 400, 600]
colors = ['teal', 'red', 'green', 'purple', 'gold']
for b0, b1, col in zip([None]+bounds, bounds+[None], colors):
    if b0 is None:
        filter = (myData.R < b1)
        label = f'$ R < {b1} $'
    elif b1 is None:
        filter = (myData.R >= b0)
        label = f'${b0} ≤ R $'
    else:
        filter = (myData.R >= b0) & (myData.R < b1)
        label = f'${b0} ≤ R < {b1}$'
    ax1.scatter(myData.X[filter], myData.Y[filter], s=20, c=col, marker='_', label=label)
ax1.legend()
plt.show()

另外,pandas 的 cut 可用于创建类别,而 seaborn 的功能(例如其 hue 参数)可以进行着色并自动创建图例。

from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns

N = 500
myData = pd.DataFrame({'X': np.round( np.random.uniform(-1000, 1000, N),-2), 'Y': np.random.uniform(-800, 800, N)})
myData['R'] = np.sqrt(myData.X ** 2 + myData.Y ** 2)

fig, ax1 = plt.subplots()

bounds = [150, 200, 400, 600]
colors = ['teal', 'red', 'green', 'purple', 'gold']

hues = pd.cut(myData.R, [0]+bounds+[2000], right=False)
sns.scatterplot(myData.X, myData.Y, hue=hues, hue_order=hues.cat.categories, palette=colors, s=20, marker='_', ax=ax1)
plt.show()

【讨论】:

  • 不错!两种方式都非常有效,我想我会选择第一种,因为我想坚持使用 MPL,而不添加新的依赖项,例如 Seaborn。非常感谢!
  • 我也找到了另一个解决方案,但它是“hacky”:matplotlib.org/3.1.1/gallery/text_labels_and_annotations/…,基本上它会独立于情节创建图例,当然按照你的建议做会更好,因为图例是直接生成的从数据中
猜你喜欢
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-10
  • 2017-06-30
  • 2011-08-29
  • 2016-08-31
  • 2020-02-15
相关资源
最近更新 更多