如何用Python绘制兼具分类色彩与连续透明度的热力图
如何用Python绘制兼具分类色彩与连续透明度的热力图
嘿,这个需求我刚好折腾过,其实不用搞你想的那种复杂自定义色卡,直接用matplotlib的基础绘图功能就能轻松实现,每个单元格单独设置颜色和透明度,思路超直接!
核心逻辑很简单:seaborn的heatmap是基于单一数值映射色卡的,没法同时独立控制颜色和透明度这两个维度。那我们换个思路——手动给每个单元格画矩形,这样每个矩形都能单独指定facecolor(填充色)和alpha(透明度),完美匹配你的需求。
直接上可运行的完整代码,我给你加了详细注释:
import pandas as pd import matplotlib.pyplot as plt from matplotlib.patches import Rectangle # 你的示例数据 colors = pd.DataFrame([['b','g','r'],['black','orange','purple'],['r','yellow','white']]) transparency = pd.DataFrame([[0.1,0.2,0.3],[0.9,0.1,0.2],[0.1,0.6,0.3]]) # 获取数据的行列数 rows, cols = colors.shape # 创建画布和绘图轴 fig, ax = plt.subplots(figsize=(6, 4)) # 每个单元格的宽高,这里设为1,你可以根据需求调整 cell_size = 1 # 遍历每一个单元格 for i in range(rows): for j in range(cols): # 取出当前单元格的颜色和透明度 current_color = colors.iloc[i, j] current_alpha = transparency.iloc[i, j] # 计算矩形的位置:matplotlib的y轴默认从下往上递增,所以要反转行索引,保证数据第一行在图的最上方 x_pos = j * cell_size y_pos = (rows - 1 - i) * cell_size # 创建矩形对象:设置填充色、透明度,再加个白色边框区分单元格 rect = Rectangle((x_pos, y_pos), cell_size, cell_size, facecolor=current_color, alpha=current_alpha, edgecolor='white') # 把矩形添加到绘图轴上 ax.add_patch(rect) # 设置坐标轴的范围,刚好覆盖所有单元格 ax.set_xlim(0, cols * cell_size) ax.set_ylim(0, rows * cell_size) # 设置刻度,让刻度标签显示在每个单元格的中间位置 ax.set_xticks([j * cell_size + cell_size/2 for j in range(cols)]) ax.set_yticks([(rows - 1 - i) * cell_size + cell_size/2 for i in range(rows)]) ax.set_xticklabels(colors.columns) ax.set_yticklabels(colors.index) # 调整刻度的位置,避免和单元格重叠 ax.tick_params(axis='both', which='major', pad=15) # 可选:添加颜色图例,方便查看每个分类对应的颜色 unique_colors = colors.stack().unique() color_legend_patches = [Rectangle((0,0), 1, 1, facecolor=color) for color in unique_colors] ax.legend(color_legend_patches, unique_colors, bbox_to_anchor=(1.05, 1), loc='upper left', title='颜色') # 自动调整布局,防止图例或刻度被截断 plt.tight_layout() plt.show()
这里有几个关键点要跟你说清楚:
alpha参数刚好是0到1的范围,和你的transparency数据完全匹配——1是完全不透明,0是完全透明,完全符合你要的“10%透明度”这种需求;- 矩形位置的计算:因为matplotlib的y轴默认是从下往上数的,而你的数据第一行要显示在图的最上面,所以用
rows - 1 - i反转了行索引,保证每个单元格的位置和你数据里的位置完全对应; - 我给每个单元格加了白色边框
edgecolor='white',这样不同单元格之间区分度更高,你要是觉得没必要可以直接删掉这个参数。
这个方法比你之前想的自定义色卡简单太多了,完全不需要把颜色转成整数再混合,每个单元格单独控制属性,想改哪个改哪个,灵活得很!
备注:内容来源于stack exchange,提问作者andbeonetraveler




