You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python matplotlib 3D散点图颜色图例仅显示首个颜色问题

问题描述

需要创建同时带有尺寸、颜色两类图例的3D散点图,但实际生成的颜色图例仅展示颜色列表中的第一个颜色,无法正确显示所有分类对应的颜色。

复现代码

import matplotlib.pyplot as plt
import matplotlib.colors
# Visualizing 5-D mix data using bubble charts
# leveraging the concepts of hue, size and depth
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
t = fig.suptitle('Wine Residual Sugar - Alcohol Content - Acidity - Total Sulfur Dioxide - Type', fontsize=14)

xs = [1,2,3,5,4]
ys = [6,7,3,5,4]
zs = [1,5,3,9,4]
data_points = [(x, y, z) for x, y, z in zip(xs, ys, zs)]

ss = [100,200,390,500,400]
colors = ['red','red','blue','yellow','yellow']

scatter = ax.scatter(xs, ys, zs, alpha=0.4, c=colors, s=ss)

ax.set_xlabel('Residual Sugar')
ax.set_ylabel('Alcohol')
ax.set_zlabel('Fixed Acidity')


legend1 = ax.legend(*scatter.legend_elements()[0],
                    loc="upper right", title="Classes", labels=colors, bbox_to_anchor=(1.5, 1),prop={'size': 20})
ax.add_artist(legend1)

# produce a legend with a cross section of sizes from the scatter
handles, labels = scatter.legend_elements(prop="sizes", alpha=0.6)
legend2 = ax.legend(handles, labels, loc="upper right", title="Sizes", bbox_to_anchor=(1.5, 0.5), prop={'size': 20})

实际异常运行效果

运行效果

问题原因

scatter.legend_elements()默认是为数值型连续颜色映射设计的,当直接传入字符串格式的分类颜色列表时,该方法无法自动识别去重后的分类条目,仅会生成单个颜色的图例元素,手动传入全量颜色作为labels参数还会出现句柄和标签匹配错位的问题,最终导致颜色图例显示异常。

修改方案

不要使用scatter.legend_elements()生成分类颜色的图例,手动对颜色分类去重后,单独创建对应颜色的图例句柄即可。

修改后完整代码

import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
# Visualizing 5-D mix data using bubble charts
# leveraging the concepts of hue, size and depth
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
t = fig.suptitle('Wine Residual Sugar - Alcohol Content - Acidity - Total Sulfur Dioxide - Type', fontsize=14)

xs = [1,2,3,5,4]
ys = [6,7,3,5,4]
zs = [1,5,3,9,4]
data_points = [(x, y, z) for x, y, z in zip(xs, ys, zs)]

ss = [100,200,390,500,400]
colors = ['red','red','blue','yellow','yellow']

scatter = ax.scatter(xs, ys, zs, alpha=0.4, c=colors, s=ss)

ax.set_xlabel('Residual Sugar')
ax.set_ylabel('Alcohol')
ax.set_zlabel('Fixed Acidity')

# 手动生成分类颜色图例
# 如需固定分类顺序,可手动指定unique_colors列表,例如 unique_colors = ['red', 'blue', 'yellow']
unique_colors = list(dict.fromkeys(colors))  # 按首次出现顺序去重
color_handles = [
    Line2D([0], [0], marker='o', color='w', markerfacecolor=c, markersize=15, alpha=0.4) 
    for c in unique_colors
]
legend1 = ax.legend(
    color_handles, unique_colors,
    loc="upper right", title="Classes", bbox_to_anchor=(1.5, 1), prop={'size': 20}
)
ax.add_artist(legend1)

# 尺寸图例保留原有逻辑即可
handles, labels = scatter.legend_elements(prop="sizes", alpha=0.6)
legend2 = ax.legend(handles, labels, loc="upper right", title="Sizes", bbox_to_anchor=(1.5, 0.5), prop={'size': 20})

plt.show()

修改说明

  • 使用dict.fromkeys(colors)对颜色列表按首次出现顺序去重,避免set去重导致顺序随机的问题
  • 通过Line2D手动创建每个分类颜色对应的图例标记,匹配散点的透明度、样式
  • 尺寸图例的原有逻辑不需要调整,不受颜色图例修改的影响

内容的提问来源于stack exchange,提问作者phw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 20:36:32