将 2 个不同的遮罩应用于 seaborn 热图或手动更改单元格的颜色

Apply 2 different masks to a seaborn heatmap or manually change the color of a cell

我有一个数据框,我想绘制 seaborn 热图:

import seaborn as sns

res = sns.heatmap(df, cmap='flare',xticklabels=1, yticklabels=1,linecolor='white',linewidths=0.5,
                 cbar=True,mask=df.isnull(),cbar_kws={'shrink': 0.6},vmin=vmin, vmax=vmax)

我已经为 NaN 单元格应用了掩码。现在,我想用颜色图中没有的自定义颜色(例如蓝色)更改几个单元格的颜色,以表明这些单元格属于另一个类别。

我的问题是: 是否可以将 2 个或更多不同颜色的蒙版应用于 seaborn 热图或手动将单元格的颜色更改为完全另一种颜色?

不清楚蓝色方块是如何表示的。以下解决方案假设它们在第二个矩阵中表示为一个。第一个热图像以前一样绘制。然后第二个热图使用一个特殊的颜色图(在这种情况下使用一种颜色,但也可以是一个完整的范围),屏蔽掉所有不应绘制的地方。

请注意,掩码可以通过 logical or(符号:|)组合。

from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
import seaborn as sns
import pandas as pd
import numpy as np

N = 10
data = np.random.uniform(0, 45, size=(N, N))
for x, y in np.random.randint(0, N, 50).reshape(-1, 2):
    data[x, y] = np.nan  # fill in some nans at random places
df = pd.DataFrame(data)
up_triang = np.triu(np.ones_like(data)).astype(bool)
ax = sns.heatmap(df, cmap='flare', xticklabels=True, yticklabels=True, square=True,
                 linecolor='white', linewidths=0.5,
                 cbar=True, mask=df.isnull() | up_triang, cbar_kws={'shrink': 0.6, 'pad': 0}, vmin=0, vmax=45)

data_special = np.random.randint(0, 5, size=(N, N)) // 4
sns.heatmap(data_special, cmap=ListedColormap(['cornflowerblue']), linecolor='white', linewidths=0.5,
            square=True, cbar=False, mask=(data_special != 1) | up_triang, ax=ax)
ax.plot([0, N, 0, 0], [0, N, N, 0], clip_on=False, color='black', lw=2)
ax.tick_params(left=False, bottom=False)
plt.show()

当特殊单元格只有一种颜色时,另一种方法是对颜色图使用“下”色,并为这些单元格赋予负值。另一个好处是颜色可以显示在颜色栏中。这是一些示例代码:

N = 10
data = np.random.uniform(0, 45, size=(N, N))
for x, y in np.random.randint(0, N, 50).reshape(-1, 2):
    data[x, y] = np.nan
data_special = np.random.randint(0, 5, size=(N, N)) // 4
data[data_special == 1] = -1
df = pd.DataFrame(data)

up_triang = np.triu(np.ones_like(data)).astype(bool)
cmap =  sns.color_palette('mako', as_cmap=True).copy()
cmap.set_under('crimson ')
ax = sns.heatmap(df, cmap=cmap, xticklabels=True, yticklabels=True, square=True,
                 linecolor='white', linewidths=0.5, cbar=True, mask=df.isnull() | up_triang,
                 cbar_kws={'shrink': 0.6, 'pad': 0, 'extend': 'min', 'extendrect': True}, vmin=0, vmax=45)
ax.plot([0, N, 0, 0], [0, N, N, 0], clip_on=False, color='black', lw=2)
ax.tick_params(left=False, bottom=False)
plt.show()