离散热图,根据变量改变单元格不透明度(seaborn?)
我想制作一个热力图,这个热力图需要同时展示连续变量和分类数据。我的目标是让颜色的深浅来自于分类,而透明度(或者说饱和度)则来自于连续值。
一个简单的数据集可能是这样的:
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)
正如你所看到的,我使用了 seaborn
和 pyplot
。我在尝试改变基本的行为时遇到了一些困难。直接设置单元格的颜色是否是正确的方向呢?
提前感谢你的回答,祝好!
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()