离散热图,根据变量改变单元格不透明度(seaborn?)

1 投票
2 回答
45 浏览
提问于 2025-04-14 16:53

我想制作一个热力图,这个热力图需要同时展示连续变量和分类数据。我的目标是让颜色的深浅来自于分类,而透明度(或者说饱和度)则来自于连续值。

一个简单的数据集可能是这样的:

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

names = ['Berthe', 'Paul', 'Francis', 'Jane']

time_spent_together = [
    [ 1.0, 0.2, 0.7, 0.5 ],
    [ 0.2, 1.0, 0.8, 0.5 ],
    [ 0.7, 0.8, 1.0, 0.1 ],
    [ 0.5, 0.5, 0.1, 1.0 ],
]

type_of_relationship = [
    [ 'id', 'friends', 'coworkers', 'nemesis' ],
    [ 'friends', 'id', 'family', 'family' ],
    [ 'coworkers', 'family', 'id', 'friends' ],
    [ 'nemesis', 'family', 'friends', 'id' ],
]

df_times = pd.DataFrame(data=time_spent_together, index=names, columns=names)
df_relationships = pd.DataFrame(data=type_of_relationship, index=names, columns=names)

而最终的结果会“改变”离散图的样子:

plt.figure(figsize=(3,3))
value_to_int = {j:i for i,j in enumerate(pd.unique(df_relationships.values.ravel()))}
n = len(value_to_int)
cmap = sns.color_palette("tab10", n) 
sns.heatmap(df_relationships.replace(value_to_int), cmap=cmap)

在这里输入图片描述

还有这个连续图:

plt.figure(figsize=(3,3))
sns.heatmap(df_times, cmap='Greys',annot=True, 
            annot_kws={"size": 7}, vmin=0.25, vmax=1)

在这里输入图片描述

正如你所看到的,我使用了 seabornpyplot。我在尝试改变基本的行为时遇到了一些困难。直接设置单元格的颜色是否是正确的方向呢?

提前感谢你的回答,祝好!

2 个回答

2

你可以把两个图重叠在一起,一个用颜色显示,另一个用一种从透明到白色的渐变色:

import matplotlib as mpl
import matplotlib.colors as mcolors

plt.figure(figsize=(4, 3))

# categories
value_to_int = {j:i for i,j in enumerate(pd.unique(df_relationships.values.ravel()))}
n = len(value_to_int)
cmap = sns.color_palette("tab10", n) 
ax = sns.heatmap(df_relationships.replace(value_to_int), cmap=cmap)

# values
c_alpha = mcolors.colorConverter.to_rgba('white', alpha=0)
c_white = mcolors.colorConverter.to_rgba('white', alpha=1)
alpha_cmap = mcolors.LinearSegmentedColormap.from_list('alpha_cmap', [c_alpha,c_white], 512)
ax2 = sns.heatmap(df_times.rsub(1), cmap=alpha_cmap, annot=df_times, 
            annot_kws={"size": 7}, vmin=0.25, vmax=1, ax=ax, cbar=None)
cbar = ax.figure.colorbar(mpl.cm.ScalarMappable(mcolors.Normalize(vmin=0, vmax=1), cmap='Greys'), ax=ax2)
cbar.ax.set_ylim(bottom=0.25)

注意,不同颜色的亮度感知会有所不同,所以在读取图表时,可能很难准确映射出一个绝对的数值。

输出结果:

这里输入图片描述

1

这里有一种方法,使用了多种单色调的颜色映射,并且配有图例:

import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
import seaborn as sns
import pandas as pd
import numpy as np

fig, ax = plt.subplots(figsize=(6, 6))

cmaps = ['Blues', 'Oranges', 'Greens', 'Reds', 'Purples']
norm = plt.Normalize(vmin=0.25, vmax=1)
handles = []
for rel, cmap in zip(np.unique(df_relationships.values), cmaps):
    sns.heatmap(df_times, mask=df_relationships.values != rel, cmap=cmap, norm=norm, annot=True, cbar=False)
    handles.append(plt.Rectangle((0, 0), 0, 0, color=plt.get_cmap(cmap)(0.55), lw=0, label=rel))

plt.colorbar(ScalarMappable(cmap='Greys', norm=norm), ax=ax)
ax.legend(handles=handles, ncol=len(handles), bbox_to_anchor=(0, 1.01), loc='lower left', handlelength=0.7)
plt.tight_layout()
plt.show()

sns.heatmap with multiple color ranges

撰写回答