如何在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()
额外建议
- 验证色盲友好性:可以用在线工具如Coblis模拟色盲视觉,检查你的配色是否符合需求;也可以用Matplotlib的
colors.rgb_to_hsv或第三方库如colorspacious来转换到色盲友好的色彩空间验证。 - 归一化权重:如果你的三个因素贡献是归一化的(即R+G+B=1),可以去掉
np.clip,因为权重和为1,基色的线性组合分量不会超出0-1范围。 - 自定义色图:如果需要批量处理,可以基于这三个基色创建自定义色图,但直接计算混合色更适合你当前的散点图场景。
备注:内容来源于stack exchange,提问作者4sght
相关产品推荐
相关产品推荐

