【问题标题】:Extracting Segmented Colormap Boundary Locations提取分段颜色图边界位置
【发布时间】:2021-10-04 12:37:25
【问题描述】:

我正在使用 Matplotlib 制作自定义颜色图。

我了解,通过颜色条,我们可以将连续范围的值映射到颜色映射中定义的颜色“光谱”。我正在制作一个自定义分段颜色图来覆盖离散的值范围。

我的代码:

colors = ['#FBE9BD', '#F2C370', '#E58B48', '#C35D30', '#A12F29']
cmap1 = LinearSegmentedColormap.from_list("mycmap", colors, N=5)

我正在着色的数据范围为 [50, 450]。我的理解是,这个颜色条然后将这个范围(最大 - 最小)划分为五个等距的子部分,并根据上面提供的数组为其分配一个颜色值。我的问题是,是否有任何简单的方法可以从此颜色图中提取边界范围?我想将每个 bin 范围(即 50-120、121-175)的值映射到一个图例出现在其各自颜色旁边。

第二个问题 - 假设我们将 N=5 的值增加到 N=10。无需在颜色数组中提供任何其他颜色。现在我们将拥有比不同颜色值更多的子类别。这里会发生什么情况,一些 bin 是否共享相同的颜色分配?

任何信息或示例的链接将不胜感激。

【问题讨论】:

    标签: matplotlib


    【解决方案1】:

    以下代码和情节试图说明正在发生的事情。

    LinearSegmentedColormap.from_list("", colors) 在内部创建一个平滑的颜色图,在这种情况下,5 种颜色的值分别为 0、0.25、0.5、0.75 和 1(即范围 0-1 均匀分布到 5-1=4 个相等的空间中。in-位置之间平滑插值。当你设置N时,N区域被放入0-1范围内。例如N=10,设置了10个区域(使用11个边界)。每个区域的颜色值为通过将 0-1 之间的平滑颜色范围划分为 N(因此,不是 N+1)相等的位置获得。

    现在,为了将外部值 50-450 映射到内部值 0-1,使用 norm。默认情况下,使用数据的最小值和最大值。 vmin 和/或vmax 可以设置明确的最小值和最大值。 (除此之外,还可以指定一个显式的范数函数。)

    要计算外部值的边界,需要将范围 50-450 切割成 N 个相等的区域,因此有 N+1 个边界。 np.linspace(50, 450, N+1) 是一个 numpy 函数,它将这些边界值创建到一个数组中。

    from matplotlib import pyplot as plt
    from matplotlib.colors import LinearSegmentedColormap
    from matplotlib.cm import ScalarMappable
    import numpy as np
    
    colors = ['#FBE9BD', '#F2C370', '#E58B48', '#C35D30', '#A12F29']
    cmap1 = LinearSegmentedColormap.from_list("mycmap1", colors, N=10)
    
    minval = 50
    maxval = 450
    bounds = np.linspace(minval, maxval, cmap1.N + 1)
    x = np.random.uniform(size=1000)
    y = np.random.uniform(minval, maxval, size=x.size)
    plt.scatter(x, y, c=y, vmin=minval, vmax=maxval, cmap=cmap1)
    
    cbar = plt.colorbar(ScalarMappable(cmap=LinearSegmentedColormap.from_list("mycmap1", colors, N=256)),
                        label='Complete colormap', ticks=np.linspace(0, 1, len(colors)))
    cbar.ax.set_yticklabels(colors)
    plt.colorbar(ticks=bounds, label=f'LinearSegmentedColormap N={cmap1.N}')
    plt.show()
    

    左边N=5,右边N=10

    下图显示了相同的情况,但颜色更易区分。平滑颜色条上的粗标记显示分段颜色条的确切颜色来源。

    PS:以类似的方式创建图例:

    from matplotlib import pyplot as plt
    from matplotlib.colors import LinearSegmentedColormap
    from matplotlib.lines import Line2D
    import numpy as np
    
    colors = ['#FBE9BD', '#F2C370', '#E58B48', '#C35D30', '#A12F29']
    
    cmap1 = LinearSegmentedColormap.from_list("mycmap1", colors, N=10)
    
    minval = 50
    maxval = 450
    bounds = np.linspace(minval, maxval, cmap1.N + 1)
    x = np.random.uniform(size=1000)
    y = np.random.uniform(minval, maxval, size=x.size)
    plt.scatter(x, y, c=y, vmin=minval, vmax=maxval, cmap=cmap1)
    
    handles = [Line2D([], [], color=cmap1(i / (cmap1.N - 1)),
                      marker='o', ls='', label=f'{bounds[i]:.0f}-{bounds[i+1]:.0f}')
               for i in range(cmap1.N) if i < 6 or i == cmap1.N-1]
    plt.legend(handles=handles, bbox_to_anchor=[1.02, 1.02], loc='upper left')
    plt.tight_layout()
    plt.show()
    

    【讨论】:

    • 感谢您如此详细的回复!这种技术是否也可以用于将值(颜色/标签)映射到图例对象而不是颜色条?
    • 我更新了代码以同时创建图例句柄和标签,并添加了一个 if-test 来过滤掉一些值。
    猜你喜欢
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多