如何使用 Seaborn 热图设置固定颜色范围?

How to set fixed color ranges with Seaborn heatmap?

我正在尝试使用 Seaborn 包制作热图,我在其中定义了自己的颜色范围。但是,我无法让图例以非连续的方式显示值。我想要以下颜色指示:

0-0.001: 绿色

0.001-0.25:黄色

0.25-0.50: 橙色

0.50-0.75:红色

0.75-1.00: 暗红色

但我明白了:

我怀疑第一个范围扰乱了画面,因为它比较小。但是,我希望图例“框”或区域的大小相同。因此是否有可能有这样或类似的东西:

我目前使用的代码如下。任何提示或建议将不胜感激。谢谢!

my_colors=['#02ab2e','gold','orange','red', 'darkred']


grid_kws = {"height_ratios": (.9, .025), "hspace": .1}
f, (ax, cbar_ax) = plt.subplots(2, gridspec_kw=grid_kws)
ax = sns.heatmap(STEdata.iloc[:,3:13].reindex(ste_order_reg.sort_values().index, axis=0), 
                 yticklabels=2, ax=ax,
                 cmap = my_colors,
                 cbar_ax=cbar_ax, 
                 cbar_kws={"orientation": "horizontal"})
# sns.set(rc = {'figure.figsize':(8, 18)})

colorbar = ax.collections[0].colorbar
colorbar.set_ticks([0, 0, 0.25, 0.5, .75])
colorbar.set_ticklabels(['0',']0-0.25]', ']0.25-0.50]',']0.50-0.75]', ']0.75-1.00]'])

要设置不均匀的颜色范围,可以使用 BoundaryNorm。颜色条刻度可以位于每个范围的中心。 ListedColormap 从颜色列表创建颜色图。

import matplotlib.pyplot as plt
from matplotlib.colors import BoundaryNorm, ListedColormap
import seaborn as sns
import numpy as np

my_colors = ['#02ab2e', 'gold', 'orange', 'red', 'darkred']
my_cmap = ListedColormap(my_colors)
bounds = [0, 0.0001, 0.25, 0.50, 0.75, 1]
my_norm = BoundaryNorm(bounds, ncolors=len(my_colors))

grid_kws = {"height_ratios": (.9, .025), "hspace": .1}
fig, (ax, cbar_ax) = plt.subplots(nrows=2, figsize=(8,18), gridspec_kw=grid_kws)
sns.heatmap(np.clip(np.random.rand(21, 12) - 0.1, 0, 1),
            yticklabels=2, 
            ax=ax,
            cmap=my_cmap,
            norm=my_norm,
            cbar_ax=cbar_ax,
            cbar_kws={"orientation": "horizontal"})

colorbar = ax.collections[0].colorbar
colorbar.set_ticks([(b0+b1)/2 for b0, b1 in zip(bounds[:-1], bounds[1:])])
colorbar.set_ticklabels(['0', ']0-0.25]', ']0.25-0.50]', ']0.50-0.75]', ']0.75-1.00]'])

plt.show()

可以创建自定义图例而不是颜色图。在热图中分配正确的颜色需要相同的 BoundaryNormax.text()ax.hlines() 可用于放置分组的文本和行。 y-axis transform 使用数据的 y-coordinates 和 x 坐标作为轴的一部分。 clip_on=False 允许在主绘图区外绘图。

import matplotlib.pyplot as plt
from matplotlib.colors import BoundaryNorm, ListedColormap
from matplotlib.lines import Line2D
import seaborn as sns
import numpy as np

unavail_color = 'lightgray'
my_colors = ['#02ab2e', 'gold', 'orange', 'red', 'darkred']
my_cmap = ListedColormap(my_colors)
bounds = [0, 0.0001, 0.25, 0.50, 0.75, 1]
my_norm = BoundaryNorm(bounds, ncolors=len(my_colors))

data = np.exp(np.random.rand(21, 12)) - 1
data[data > 1] = np.nan

fig, ax = plt.subplots(figsize=(8, 18))
ax.set_facecolor(unavail_color)
sns.heatmap(data,
            yticklabels=2, ax=ax,
            cmap=my_cmap,
            norm=my_norm,
            cbar=False)
ax.tick_params(labelsize=16)

group_edges = np.array([0, 5, 6, 13, 14, 21])
group_labels = ['group A', '', 'group B', '', 'group C']
ax.hlines(group_edges, np.zeros(len(group_edges)), np.zeros(len(group_edges)) - 0.12,
          color='navy', lw=2, clip_on=False, transform=ax.get_yaxis_transform())
for label, b0, b1 in zip(group_labels, group_edges[:-1], group_edges[1:]):
    ax.text(-0.12, (b0 + b1) / 2, label, color='navy', fontsize=20, ha='left', va='center',
            rotation=90, transform=ax.get_yaxis_transform())

handles = [Line2D([], [], lw=10, color=color, label=label)
           for color, label in zip(my_colors + [unavail_color],
                                   ['0', ']0-0.25]', ']0.25-0.50]', ']0.50-0.75]', ']0.75-1.00]', 'unavailable'])]
ax.legend(handles=handles, handlelength=0.5, ncol=len(my_colors) + 1,
          bbox_to_anchor=(0.5, -0.02), loc='upper center', frameon=False)

plt.tight_layout()
plt.show()