如何基于指定分组创建层次聚类热图?含预处理验证
问题描述
我想要创建特定样式的热图:右侧色带分为4个分组,分别为Control > Rv, Ra、Rv> Ra > Control、Ra > Control > Rv、Ra > Rv > Control。该热图基于Euclidean距离矩阵构建,数据需经过FPKM归一化、log2转换(加1)及均值中心化处理。
示例FPKM表格(实际为85行×3列)
| gene | Control | Ra | Rv |
|---|---|---|---|
| Cyp27a1 | 32.681000 | 4.52616 | 13.3868 |
| Serpinb2 | 1.611980 | 513.10400 | 20.7699 |
| Rab7b | 118.964000 | 20.78690 | 43.3432 |
| Ptgs2 | 0.328475 | 371.33000 | 29.0941 |
| Mgat4a | 6.680070 | 70.09530 | 25.931 |
| Acsl1 | 15.459900 | 431.43400 | 186.2550 |
| Gas6 | 82.042200 | 8.39169 | 17.3220 |
| Bcl2a1a | 4.162480 | 395.83800 | 99.9439 |
| Zc3h12c | 7.454010 | 43.01970 | 18.7162 |
| Bcl2a1d | 15.641200 | 440.34900 | 109.8670 |
已完成代码
df.set_index('gene', inplace=True) df[['Control', 'Ra','Rv']] = df_deg[['Control', 'Ra','Rv']].add(1).apply(np.log2) # FPKM +1 and log transformed df_cent = df.apply(lambda x:x-x.mean()) # mean centered ## Heatmap ## map_plt= sns.clustermap(df_cent, metric="euclidean", cmap="RdBu_r", vmin=-8, vmax=6, xticklabels=True, yticklabels=False) map_plt.ax_col_dendrogram.set_visible(False) plt.show()
当前生成的热图效果不符合预期,需手动调整vmin和vmax避免色块单一。现需解决两个问题:
- 如何按上述指定分组对热图进行排列?
- 我的log2转换与均值中心化步骤是否正确?
解决方案
1. 按指定分组排列热图
要实现自定义分组排序,需要先为每个基因匹配对应分组标签,再按指定顺序排序数据,最后关闭行聚类以保留自定义顺序,同时添加侧边分组色带。
实现代码
import seaborn as sns import numpy as np import pandas as pd import matplotlib.pyplot as plt from matplotlib.patches import Patch # 1. 为基因分配分组标签 def assign_group(row): ctrl, ra, rv = row['Control'], row['Ra'], row['Rv'] if ctrl > rv and ctrl > ra: return 'Control > Rv, Ra' elif rv > ra and rv > ctrl: return 'Rv> Ra > Control' elif ra > ctrl and ra > rv: if ctrl > rv: return 'Ra > Control > Rv' else: return 'Ra > Rv > Control' else: return 'Other' # 处理表达值相等的边界情况 df_cent['group'] = df_cent.apply(assign_group, axis=1) # 2. 按指定分组顺序排序数据 target_order = ['Control > Rv, Ra', 'Rv> Ra > Control', 'Ra > Control > Rv', 'Ra > Rv > Control'] group_rank = {g:i for i,g in enumerate(target_order)} df_sorted = df_cent.sort_values(by='group', key=lambda x: x.map(group_rank)) # 3. 准备分组侧边色带的颜色映射 group_colors = { 'Control > Rv, Ra': '#1f77b4', 'Rv> Ra > Control': '#ff7f0e', 'Ra > Control > Rv': '#2ca02c', 'Ra > Rv > Control': '#d62728' } row_colors = df_sorted['group'].map(group_colors) # 4. 绘制热图(关闭行聚类,保留自定义顺序) map_plt = sns.clustermap( df_sorted.drop('group', axis=1), metric="euclidean", cmap="RdBu_r", xticklabels=True, yticklabels=False, row_cluster=False, # 关闭行聚类,使用自定义排序 row_colors=row_colors # 添加侧边分组色带 ) map_plt.ax_col_dendrogram.set_visible(False) # 添加分组图例 handles = [Patch(color=c, label=g) for g,c in group_colors.items()] map_plt.ax_heatmap.legend(handles=handles, bbox_to_anchor=(1.25, 1), loc='upper left') plt.show()
2. log2转换与均值中心化步骤检查
你的代码存在两个细节问题,核心逻辑是正确的:
- 变量名不一致:代码中用
df_deg做转换,但前面是对df设置索引,会导致处理错误数据或报错,需统一变量名 - 均值中心化方向:
df.apply(lambda x:x-x.mean())默认按行处理(每个基因自身均值中心化),符合热图常规需求,但建议明确指定axis=1增强可读性
修正后的预处理代码
df.set_index('gene', inplace=True) # 统一使用df,避免变量名错误 df[['Control', 'Ra','Rv']] = df[['Control', 'Ra','Rv']].add(1).apply(np.log2) # FPKM+1后log2转换 df_cent = df.apply(lambda x: x - x.mean(), axis=1) # 明确指定行方向的均值中心化
另外,关于vmin和vmax的手动调整问题,可以通过计算数据全局极值自动设置:
# 取对称边界,避免色块单一 abs_max = np.max(np.abs(df_cent.values)) vmin, vmax = -abs_max, abs_max
内容的提问来源于stack exchange,提问作者Caroline
相关产品推荐
相关产品推荐

