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

如何在Matplotlib的ax.scatter中实现色盲友好的RGB色彩方案?

如何在Matplotlib的ax.scatter中实现色盲友好的RGB色彩方案?

你遇到的问题其实很典型——直接用color参数手动指定RGB颜色时,cmap参数会被Matplotlib忽略,因为cmap是专门为数值映射颜色(通过c参数传入数值数组)设计的,不会覆盖你直接传入的颜色值。要改成色盲友好的配色,核心思路是把原来的RGB基色替换成色盲友好的三元色,再用三个因素的权重去混合这些新基色,而不是依赖色图映射。

具体解决方案步骤

1. 选择色盲友好的三元基色

最常见的色盲类型是红绿色盲,我们需要选三个在色盲视觉下区分度高的基色。比如推荐这套配色:

  • 对应原红色通道(Variable a):橙色 #E69F00(RGB:[0.9, 0.6, 0.0])
  • 对应原绿色通道(Variable b):青色 #56B4E9(RGB:[0.337, 0.706, 0.914])
  • 对应原蓝色通道(Variable c):紫色 #990099(RGB:[0.6, 0.0, 0.6])

这三个颜色在红绿色盲、蓝黄色盲视觉下都能清晰区分,符合你的需求。

2. 修改颜色混合逻辑

原来你是用(R, G, B)直接作为颜色,现在改成用三个因素的权重去混合新的基色:比如原(i/100, 0, j/100)对应i%的原红色 + j%的原蓝色,现在替换成i%的橙色 + j%的紫色,再把混合后的颜色分量限制在0-1之间(避免超出RGB色域)。

3. 同步更新3D图例的箭头颜色

把原来的灰色箭头改成对应基色,让图例和散点的配色逻辑保持一致,用户更容易理解。

修改后的完整代码

import numpy as np # version 1.19.2
import matplotlib as mp # version 3.3.2
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d

####### 3D cube legend ####
class Arrow3D(FancyArrowPatch):
    def __init__(self, xs, ys, zs, *args, **kwargs):
        FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)
        self._verts3d = xs, ys, zs

    def draw(self, renderer):
        xs3d, ys3d, zs3d = self._verts3d
        xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
        self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
        FancyArrowPatch.draw(self, renderer)

# 定义色盲友好的三个基色(对应原红、绿、蓝通道)
base_a = np.array([0.9, 0.6, 0.0])    # 橙色 → 对应Variable a
base_b = np.array([0.337, 0.706, 0.914]) # 青色 → 对应Variable b
base_c = np.array([0.6, 0.0, 0.6])    # 紫色 → 对应Variable c

fig1 = plt.figure()
ax2 = fig1.add_axes([0.,0.,1,1],projection='3d')
r = np.arange(0,101,1)[::2]

ax2.invert_xaxis()
intr = 4
X, Y = np.meshgrid(r, r)
XX,YY = np.meshgrid(r[::-1],r[::-1])
rot = 40
for i in r:
    for j in r:
        # 替换原(i/100,0,j/100)的颜色:混合base_a和base_c
        color1 = (i/100.) * base_a + (j/100.) * base_c
        color1 = np.clip(color1, 0, 1)  # 限制颜色分量在0-1之间
        ax2.scatter(i,0,j,s=50,color=color1,marker=(4,0,rot))
        
        # 替换原(i/100,j/100,0)的颜色:混合base_a和base_b
        color2 = (i/100.) * base_a + (j/100.) * base_b
        color2 = np.clip(color2, 0, 1)
        ax2.scatter(i,j,0,s=50,color=color2,marker=(4,0,rot))
        
        # 替换原(0,i/100,j/100)的颜色:混合base_b和base_c
        color3 = (i/100.) * base_b + (j/100.) * base_c
        color3 = np.clip(color3, 0, 1)
        ax2.scatter(0,i,j,s=50,color=color3,marker=(4,0,rot))

ax2.view_init(-30,-45)
# 把箭头改成对应基色,增强图例可读性
a = Arrow3D([0,110],[0,0],[0,0],mutation_scale=20,lw=3,arrowstyle='-|>',zorder=6*len(r)**2+1,color=base_a)
ax2.add_artist(a)
a = Arrow3D([0,0],[0,110],[0,0],mutation_scale=20,lw=3,arrowstyle='-|>',zorder=6*len(r)**2+1,color=base_b)
ax2.add_artist(a)
a = Arrow3D([0,0],[0,0],[0,110],mutation_scale=20,lw=3,arrowstyle='-|>',zorder=6*len(r)**2+1,color=base_c)
ax2.add_artist(a)
ax2.patch.set_visible(False)

for i in np.arange(10,110,10):
    ax2.plot([0,101.5],[i,i],[0,0],lw=1,color='grey',zorder=6*len(r)**2+1,linestyle='--') # x
    ax2.plot([0,101.5],[0,0],[i,i],lw=1,color='grey',zorder=6*len(r)**2+1,linestyle='--') # x
    ax2.plot([i,i],[0,101.5],[0,0],lw=1,color='grey',zorder=6*len(r)**2+1,linestyle='--') # y
    ax2.plot([0,0],[0,101.5],[i,i],lw=1,color='grey',zorder=6*len(r)**2+1,linestyle='--') # y
    ax2.plot([0,0],[i,i],[0,101.5],lw=1,color='grey',zorder=6*len(r)**2+1,linestyle='--') # z
    ax2.plot([i,i],[0,0],[0,101.5],lw=1,color='grey',zorder=6*len(r)**2+1,linestyle='--') # z

for i in np.arange(20,120,20):
    ax2.text(i+5,110,0,str(float(i)/400.),zorder=6*len(r)**2+2,fontsize=12)
    ax2.text(115,i,0,str(float(i)/100.),zorder=6*len(r)**2+2,fontsize=12)
    ax2.text(0,105,i,str(float(i)/100.),zorder=6*len(r)**2+2,fontsize=12)

ax2.text2D(0.07,0.04,r"Variable a",zorder=6*len(r)**2+3,transform=ax2.transAxes,rotation=-23,fontsize=35)
ax2.text2D(0.56,0.04,r"Variable b",zorder=6*len(r)**2+3,transform=ax2.transAxes,rotation=23,fontsize=35)
ax2.text2D(0.96,0.33,r"Variable c",zorder=6*len(r)**2+3,transform=ax2.transAxes,rotation=-88,fontsize=35)

plt.axis("off")
fig1.set_size_inches(11, 9)
plt.draw()
plt.show()

额外建议

  1. 验证色盲友好性:可以用在线工具如Coblis模拟色盲视觉,检查你的配色是否符合需求;也可以用Matplotlib的colors.rgb_to_hsv或第三方库如colorspacious来转换到色盲友好的色彩空间验证。
  2. 归一化权重:如果你的三个因素贡献是归一化的(即R+G+B=1),可以去掉np.clip,因为权重和为1,基色的线性组合分量不会超出0-1范围。
  3. 自定义色图:如果需要批量处理,可以基于这三个基色创建自定义色图,但直接计算混合色更适合你当前的散点图场景。

备注:内容来源于stack exchange,提问作者4sght

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 18:07:58