堆叠颜色映射

2 投票
2 回答
1281 浏览
提问于 2025-04-17 19:01

有没有简单的方法可以把两个现有的颜色图叠加在一起,形成一个新的颜色图?

我想做的是另一个颜色编码的散点图,其中颜色对应的变量从很大的负值到很大的正值变化。我想让接近零的值颜色淡一些——简单来说,我希望能从一个现成的颜色图中(比如说,cm.Blues_r)为负值选择颜色,而从另一个颜色图中(比如说,cm.Oranges)为正值选择颜色。

2 个回答

0

我觉得自己制作颜色映射表会更简单,尤其是颜色不多的时候。这个颜色映射表是橙色、白色和蓝色。

cdict = {'red':   [ (0.0,   0.0, 0.0),
                    (0.475, 1.0, 1.0),
                    (0.525, 1.0, 1.0),
                    (1.0,   1.0, 1.0)
                  ],
         'green': [ (0.0,   0.0, 0.0),
                    (0.475, 1.0, 1.0),
                    (0.525, 1.0, 1.0),
                    (1.0,   0.65, 0.0)
                  ],
         'blue':  [ (0.0,   1.0, 1.0),
                    (0.475, 1.0, 1.0),
                    (0.525, 1.0, 1.0),
                    (1.0,   0.0, 0.0)
                  ]
}
rwb_cmap = matplotlib.colors.LinearSegmentedColormap(name = 'rwb_colormap', colors = cdict, N = 256)

颜色映射表其实就是一个字典,用来存储RGB颜色值。对于每种颜色,会有一个包含不同段落的列表。每个段落是沿着z轴的一个点,范围从0到1。颜色的变化是根据这些段落来计算的。

segment z-axis  end      start
i       z[i]    v0[i]    v1[i]
i+1     z[i+1]  v0[i+1]  v1[i+1]   
i+2     z[i+2]  v0[i+2]  v1[i+2]   

z[i]z[i+1]之间的层级,会有v1[i]v0[i+1]之间的颜色等等。这就让我们可以“跳跃”颜色。v0[0]v1[-1]是不会被使用的。你可以根据需要使用任意数量的段落。(改编自这里:http://matplotlib.org/api/colors_api.html#matplotlib.colors.LinearSegmentedColormap

N是量化级别的数量。所以如果N = 256,它会为256个级别进行插值。我用256是因为懒惰。我想当你设置N = 6并且制作4个轮廓时,你得小心。

0.475和0.525是为了确保中间的轮廓确实是白色的。对于级别[-1.5, -0.5, 0.5, 1.5],填充的颜色现在是橙色、白色和蓝色。如果我用0.5,那个中间的级别就会是蓝色和橙色的混合。

橙色的RGB代码是255-165-0,或者如果按0到1的比例来看,就是1-0.65-0。

1

这段内容没有经过测试,但作为初步尝试,我会建议你创建一个简单的 colors.Colormap 的子类。

class split_cmap(colors.Colormap):
    def __init__(self, cmap_a, cmap_b, split=.5):
        '''Makes a split color map cmap_a is the low range, 
           cmap_b is the high range
           split is where to break the range
        '''
        self.cmap_a, self.cmap_b = cmap_a, cmap_b
        self.split = split

    def __call__(self, v):
        if v < self.split:
            return self.cmap_a(v) 
            # or you might want to use v / self.split
        else:
            return self.cmap_b(v) 
            # or you might want to use (v - self.split) / (1 - self.split)

    def set_bad(self,*args, **kwargs):
        self.cmap_a.set_bad(*args, **kwargs)
        self.cmap_b.set_bad(*args, **kwargs)

    def set_over(self, *args, **kwargs):
        self.cmap_a.set_over(*args, **kwargs) # not really needed
        self.cmap_b.set_over(*args, **kwargs)

    def set_under(self, *args, **kwargs):
        self.cmap_a.set_under(*args, **kwargs)
        self.cmap_b.set_under(*args, **kwargs) # not really needed

    def is_gray(self):
        return False

colors.Colormap 类的定义

你还需要深入了解 Normalize 类。颜色映射只知道范围是 [0, 1],所以你需要确保你的 norm 在你想要改变的地方映射到 .5

你可能可以将这个功能扩展到接受一个映射列表和分割点,这样就可以有任意数量的颜色映射了。这也需要进行各种合理性检查。

如果你重新规范化输入,你还可以利用这个方法制作任何现有颜色映射的周期性版本,只需传入颜色映射及其反向映射即可。

撰写回答