GENE

      基因(Gene),一词是希腊语,意为“生”。

       研究基因,真的可以预知我们的未来吗?

       从受精的那一刻起,人类的身上就承载了历史的“密码”,这些密码,就是由基因组成的。

       这些基因不仅仅决定了我们的形体、体制,也决定了我们的性格、暴力倾向,甚至,这些基因就表明了

我们的未来。

       研究这些基因,可以研究人类的发展史,人类的迁徙史,这些信息就是保存在我们体内的密码数据,就仿佛

计算机中使用了某种方法加密过的数据,需要通过相应的算法进行解密一样;研究基因,会让我们通晓我们的过去,

隐藏在我们身上的故事。

      基因解码,洞悉过去,探究未来,研究进行时.......

 

import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from matplotlib.patches import Rectangle import warnings from scipy.cluster.hierarchy import linkage, dendrogram, leaves_list import itertools import itertools import matplotlib.transforms as mtrans from matplotlib.font_manager import FontProperties # 用于控制字体样式 warnings.filterwarnings('ignore') id_name_or_path = 'gene/.ipynb_checkpoints/scz_for_result/id_name_filter2_or.csv' id_name_p_path = 'gene/.ipynb_checkpoints/scz_for_result/id_name_filter2_p.csv' id_name_or_order = 'gene/.ipynb_checkpoints/scz_for_result/id_name_or_order2.csv' id_name_p_order = 'gene/.ipynb_checkpoints/scz_for_result/id_name_p_order2.csv' path_or = "gene/.ipynb_checkpoints/scz_for_result/overlap_or_conditional_filter_2.csv" path_p = "gene/.ipynb_checkpoints/scz_for_result/overlap_neglog10p_filter_2.csv" txt_file1 = "gene/SCZ_AD_genes/SCZ_EGS_Singh_00001.txt" # 第一个TXT文件 txt_file2 = "gene/SCZ_AD_genes/SCZ_GWAS_Trubetskoy_prioritized.txt" # 第二个TXT文件 gene_id_name_path = 'gene/.ipynb_checkpoints/id_name/ID2Symbol.txt' #加* list1_gene_names_1 = [] # 初始化,后续通过ID→名映射填充 list1_gene_names_2 = [] def get_gene_id_lists(txt1_path, txt2_path): with open(txt1_path, 'r', encoding='utf-8') as f1: # 读取所有行→去除每行首尾空格→过滤空行→转为集合(自动去重) gene_set1 = {line.strip() for line in f1 if line.strip()} with open(txt2_path, 'r', encoding='utf-8') as f2: gene_set2 = {line.strip() for line in f2 if line.strip()} common_genes = list(gene_set1 & gene_set2) # 交集:共现ID only_txt1_genes = list(gene_set1 - gene_set2) # 差集:仅第一个文件独有ID return common_genes, only_txt1_genes # ---------------------- 新增函数:基因ID转换为基因名(复用原映射规则) ---------------------- def convert_gene_id_to_name(gene_id_list, mapping_file_path): mapping_df = pd.read_csv(mapping_file_path, sep='\t') mapping_dict = {} for _, row in mapping_df.iterrows(): gene_id = str(row['id']) gene_name = row['name'] mapping_dict[gene_id] = gene_name converted_names = [] for gene_id in gene_id_list: gene_id_str = str(gene_id) # 统一转为字符串匹配 if gene_id_str in mapping_dict: converted_names.append(mapping_dict[gene_id_str]) else: converted_names.append(str(gene_id)) # 未匹配到的ID保留原形式 print(f"警告:基因ID {gene_id} 未在映射表中找到对应的基因名,将保留原ID") return converted_names def id_name_mapping(path, outpath): df = pd.read_csv(path, header=0, sep=' ') mapping_df = pd.read_csv('gene/.ipynb_checkpoints/id_name/ID2Symbol.txt', sep='\t') mapping_dict = dict(zip(mapping_df['id'], mapping_df['name'])) df['dataset'] = df['dataset'].map(mapping_dict) df.set_index('dataset', inplace=True) mapping_df_1 = pd.read_excel('gene/gene_mapping_x.xlsx') mapping_dict_1 = dict(zip(mapping_df_1['id'], mapping_df_1['name'])) df.columns = df.columns.astype(int) columns_to_replace = df.columns[0:] partial_map = {id: name for id, name in mapping_dict_1.items() if id in columns_to_replace} df = df.rename(columns=partial_map) df.to_csv(outpath, sep=' ') def genes_cell_order(path, outpath): df = pd.read_csv(path, header=0, sep=' ', index_col=0) df_gene_order = pd.read_csv("gene/.ipynb_checkpoints/clusters/gene_cluster_order_2.csv") target_gene_order = df_gene_order["gene_order"].tolist() df_cell_order = pd.read_csv("gene/.ipynb_checkpoints/clusters/cell_cluster_order.csv") target_cell_order = df_cell_order["cell_order"].tolist() df_reordered = df.reindex(target_gene_order) df_reordered = df_reordered.reindex(columns=target_cell_order) df_reordered.to_csv(outpath, sep=' ') def read_matrix_without_group(path): df = pd.read_csv(path, keep_default_na=True, sep=' ', header=0) if "dataset" not in df.columns: original_first_col = df.columns[0] df.rename(columns={original_first_col: "dataset"}, inplace=True) mat = df.copy() mat.set_index("dataset", inplace=True) return {"mat": mat} common_ids, only_first_ids = get_gene_id_lists(txt_file1, txt_file2) id_name_mapping(path_or, id_name_or_path) id_name_mapping(path_p, id_name_p_path) genes_cell_order(id_name_or_path, id_name_or_order) genes_cell_order(id_name_or_path, id_name_p_order) or_list = read_matrix_without_group(id_name_or_order) p_list = read_matrix_without_group(id_name_p_order) list1_gene_names_1 = convert_gene_id_to_name(common_ids, gene_id_name_path) list1_gene_names_2 = convert_gene_id_to_name(only_first_ids, gene_id_name_path) print(f"\n转换后需要加**的基因名列表(list1):{list1_gene_names_1}") print(f"\n转换后需要加**的基因名列表(list1):{list1_gene_names_2}") or_mat = or_list["mat"].copy() neglogp_mat = p_list["mat"].copy() common_rows = list(set(or_mat.index) & set(neglogp_mat.index)) common_cols = list(set(or_mat.columns) & set(neglogp_mat.columns)) common_rows = [row for row in or_mat.index if row in common_rows] common_cols = [col for col in or_mat.columns if col in common_cols] or_mat = or_mat.loc[common_rows, common_cols].copy() neglogp_mat = neglogp_mat.loc[common_rows, common_cols].copy() y_levels = common_rows x_levels = common_cols df_or = or_mat.reset_index().melt( id_vars="dataset", var_name="cell_type", value_name="OR" ) df_neglogp = neglogp_mat.reset_index().melt( id_vars="dataset", var_name="cell_type", value_name="neglog10p" ) df = pd.merge(df_or, df_neglogp, on=["dataset", "cell_type"], how="outer") df["neglog10p"].fillna(0, inplace=True) df["cell_type"] = pd.Categorical(df["cell_type"], categories=x_levels, ordered=True) df["dataset"] = pd.Categorical(df["dataset"], categories=list(reversed(y_levels)), ordered=True) or_breaks = [-np.inf, 1, 2, 3, 4, 5, np.inf] or_labels = ["<1", "1-2", "2-3", "3-4", "4-5", ">5"] df["OR_cat"] = pd.cut( df["OR"], bins=or_breaks, labels=or_labels, right=False, include_lowest=True, ordered=True ) # -log10(P)值分级 p_breaks = [-np.inf, 1, 2, 3, 4, np.inf] p_labels = [ "<1", "1-2", "2-3", "3-4", ">4"] df["neglog10p_cat"] = pd.cut( df["neglog10p"], bins=p_breaks, labels=p_labels, right=False, include_lowest=True, ordered=True ) or_colors = { "<1": "#FFF5F5", "1-2": "#EFA2A2", "2-3": "#EF5E5E", "3-4": "#F64343", "4-5": "#D51B1B", ">5": "#580707FB" } p_sizes = { "<1": 1.2, "1-2": 1.75, "2-3": 2.5, "3-4": 3, ">4": 4 } df["size_val"] = df["neglog10p_cat"].map(p_sizes) or_mat_1 = or_mat.fillna(0) # 生成基因(行)和细胞(列)的聚类树 gene_linkage = linkage(or_mat_1, method="average", metric="euclidean") gene_order = leaves_list(gene_linkage) # 提取聚类后的行顺序 print(gene_linkage) cell_linkage = linkage(or_mat_1.T, method="average", metric="euclidean") # fig,ax = plt.subplots( # nrows=1, # 无分组:仅1行1列的单个子图 # ncols=1, # figsize=(15, 25), # 画布大小:宽10,高6 # ) # 绘制 clustermap 作为基础,包含聚类树 g = sns.clustermap( or_mat, row_linkage=gene_linkage, col_linkage=cell_linkage, cmap="coolwarm", center=0, figsize=(15, 15), dendrogram_ratio=(0.1, 0.1), cbar_pos=None, # 不显示默认颜色条,后续自定义 row_cluster=True, col_cluster=True, xticklabels=True, yticklabels=True, annot=False ) g.ax_heatmap.set_ylim(or_mat.shape[0], 0) # 从0到基因数,完整显示 # 获取热图的轴 ax_heatmap = g.ax_heatmap # 绘制背景网格和散点 all_pairs = list(itertools.product(x_levels, y_levels)) all_comb = pd.DataFrame(all_pairs, columns=["cell_type", "dataset"]) all_comb["cell_type"] = pd.Categorical(all_comb["cell_type"], categories=x_levels, ordered=True) all_comb["dataset"] = pd.Categorical(all_comb["dataset"], categories=y_levels, ordered=True) for _, row in all_comb.iterrows(): x = x_levels.index(row["cell_type"]) y = list(reversed(y_levels)).index(row["dataset"]) rect = Rectangle((x - 0.5, y - 0.5), 1, 1, facecolor="white", edgecolor="grey", linewidth=0.6) ax_heatmap.add_patch(rect) for or_cat in or_labels: df_cat = df[df["OR_cat"] == or_cat].copy() if not df_cat.empty: x = df_cat["cell_type"].map(lambda x: x_levels.index(x)) y = df_cat["dataset"].map(lambda y: list(reversed(y_levels)).index(y)) size_values = df_cat["size_val"].astype(int) ax_heatmap.scatter( x, y, s=size_values * 30, c=or_colors[or_cat], alpha=0.8, edgecolors="black", linewidth=0.3, label=or_cat ) right_bar_path = 'gene/gene_numbercell.xlsx' # ---------------------- 右侧紧凑柱状图:读取Excel数据+绘制 ---------------------- right_bar_df = pd.read_excel(right_bar_path, header=0) # 读取柱状图数据 right_bar_dict = dict(zip(right_bar_df.iloc[:, 0], right_bar_df.iloc[:, 1])) right_bar_values = [right_bar_dict.get(gene, 0) for gene in y_levels] # 按基因顺序提取数值 # ---------------------- 调整坐标轴标签 ---------------------- # X轴标签 labels_x = ax_heatmap.get_xticklabels() dx = 6/72 dy = 5/72 offset = mtrans.ScaledTranslation(dx, dy, g.fig.dpi_scale_trans) for label in labels_x: label.set_transform(label.get_transform() + offset) ax_heatmap.set_xticklabels( x_levels, rotation=45, ha="right", va="top", fontproperties=FontProperties(style='italic', size=8), color="black" ) ax_heatmap.set_xlim(-0.5, len(x_levels) - 0.5) # Y轴标签(避开右侧柱状图) ax_heatmap.set_yticklabels([]) ax_heatmap.yaxis.tick_right() ax_heatmap.yaxis.set_label_position("right") ax_heatmap.set_ylabel('') ax_heatmap.set_yticks(range(len(y_levels))) # 字体样式 gene_font = FontProperties(style='italic', size=8, weight='normal') symbol_font = FontProperties(style='italic', size=8, weight='bold') # 生成基因标签(含*标记) gene_label_parts = [] for gene in reversed(y_levels): if gene in list1_gene_names_1: gene_label_parts.append((gene, "**")) elif gene in list1_gene_names_2: gene_label_parts.append((gene, "*")) else: gene_label_parts.append((gene, "")) # 绘制Y轴标签(向右移动,与柱状图紧凑对齐) for y_pos, (gene_name, symbol) in enumerate(gene_label_parts): ax_heatmap.text( x=len(x_levels) -0.05, # 紧贴热图右侧 y=y_pos, s=gene_name, fontproperties=gene_font, color="black", ha="left", va="center" ) if symbol: gene_length = len(gene_name) * 0.25 ax_heatmap.text( x=len(x_levels) + 0.1 + gene_length - 0.1, y=y_pos, s=symbol, fontproperties=symbol_font, color="black", ha="left", va="center" ) ax_heatmap.set_ylim(-0.5, len(y_levels) - 0.5) # ax_heatmap.set_xticks(range(len(x_levels))) # # 调整坐标轴范围 # # ax_heatmap.set_yticks(range(len(gene_order_aligned))) # # 调整 y 轴标签(核心修改:斜体基因名+粗体*标记,删除dataset标志) # # 清除默认y轴标签和刻度标签 # ax_heatmap.set_yticklabels([]) # 清除原有标签 # ax_heatmap.yaxis.tick_right() # ax_heatmap.yaxis.set_label_position("right") # ax_heatmap.set_ylabel('') # 删除y轴dataset标志 # # 确保y轴刻度与基因数量匹配 # ax_heatmap.set_yticks(range(len(y_levels))) # # 定义字体样式:基因名普通斜体,*符号粗体斜体 # gene_font = FontProperties(style='italic', size=8, weight='normal') # 基因名:斜体 # symbol_font = FontProperties(style='italic', size=8, weight='bold') # *符号:斜体+粗体 # # 生成基因名与标记的对应关系(按y轴显示顺序) # gene_label_parts = [] # for gene in reversed(y_levels): # 保持原y轴顺序 # if gene in list1_gene_names_1: # gene_label_parts.append((gene, "**")) # 列表1:加** # elif gene in list1_gene_names_2: # gene_label_parts.append((gene, "*")) # 列表2:加* # else: # gene_label_parts.append((gene, "")) # 无标记 # # 逐个绘制y轴标签(基因名+粗体*) # for y_pos, (gene_name, symbol) in enumerate(gene_label_parts): # # 绘制基因名(斜体) # x_pos = len(x_levels) # 基于细胞数量动态定位 # ax_heatmap.text( # x=x_pos-0.2, # 调整x坐标使标签在y轴右侧 # y=y_pos, # s=gene_name, # fontproperties=gene_font, # color="black", # ha="left", # 右对齐,与*衔接 # va="center" # 垂直居中 # ) # # 绘制*符号(粗体) # if symbol: # gene_length = len(gene_name) *0.25 # 估算基因名宽度(可微调系数) # ax_heatmap.text( # x=x_pos + gene_length -0.1 , # y=y_pos, # s=symbol, # fontproperties=symbol_font, # color="black", # ha="left", # 左对齐,与基因名衔接 # va="center" # ) # ax_heatmap.set_ylim(-0.5, len(y_levels) - 0.5) # 确保标签完整显示 # # 调整 x 轴标签 # labels_x= ax_heatmap.get_xticklabels() # dx = 6/72 # dy = 5/72 # offset = mtrans.ScaledTranslation(dx, dy, g.fig.dpi_scale_trans) # for label in labels_x: # label.set_transform(label.get_transform() + offset) # ax_heatmap.set_xticklabels(x_levels, rotation=45, ha="right", va="top",fontproperties=FontProperties(style='italic', size=8),fontsize=8, color="black") # ax_heatmap.set_xlim(-0.5, len(x_levels) -0.5) # 右侧预留足够空间 # 图例和布局调整 or_legend_base_size = p_sizes["1-2"] * 30 / 10 handles_or = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=or_colors[cat], markersize=or_legend_base_size * 1.5, label=cat, markeredgecolor='black', markeredgewidth=0.2) for cat in or_labels] handles_p = [] for cat in p_labels: if cat in p_sizes: size = or_legend_base_size * (p_sizes[cat] / p_sizes["1-2"]) else: size = or_legend_base_size * (0.3 / p_sizes["1-2"]) handle = plt.Line2D( [0], [0], marker='o', color='w', markerfacecolor='grey', markersize=size, label=cat, markeredgecolor='black', markeredgewidth=0.2 ) handles_p.append(handle) legend_or = g.fig.legend(handles_or, or_labels, title="Odds ratio", title_fontsize=11, loc="center right", bbox_to_anchor=(0.93, 0.05), fontsize=8) legend_p = g.fig.legend(handles_p, p_labels, title=r'$-log_{10}(p)$', title_fontsize=11, loc="center right", bbox_to_anchor=(0.99, 0.05), fontsize=8) plt.subplots_adjust(right=0.85, left=0.1, top=0.85, bottom=0.1) g.savefig("gene/.ipynb_checkpoints/png/cluster_plot.png", dpi=300, bbox_inches="tight")右侧要添加的是柱状图,数据在xlsx里,柱状图最左边与y轴基因名称最后面对应,柱状图最右边到整个图的边缘
10-29
<think>根据用户需求,我们需要在已有的热图(用基因表达数据绘制)右侧添加一个柱状图(数据来自Excel文件)。柱状图要求左对齐y轴基因名称,右边缘到整个图的边缘。 思路: 1. 首先绘制热图(例如使用seaborn的heatmap),并获取热图的y轴刻度位置(即基因名称的位置)。 2. 然后,在热图的右侧创建一个新的坐标轴用于绘制柱状图。 3. 确保柱状图的y轴刻度与热图的y轴刻度对齐(即相同的基因顺序和位置)。 4. 柱状图的数据从指定的Excel文件中读取,并且需要与热图的基因顺序一致。 步骤: 1. 读取热图数据(假设是基因表达矩阵)和柱状图数据(来自Excel,包含每个基因的某个数值)。 2. 确保柱状图数据与热图数据的基因顺序一致(按照热图的y轴标签顺序)。 3. 使用matplotlib创建图形和两个子图(或使用subplots创建主区域和右侧的坐标轴)。 4. 绘制热图在左侧坐标轴。 5. 在右侧坐标轴绘制柱状图(水平条形图,因为基因名称在y轴)。 注意:热图通常使用sns.heatmap绘制,它会返回一个坐标轴对象。我们可以利用这个对象来创建右侧共享y轴的坐标轴。 具体步骤: 1. 创建图形和主坐标轴(用于热图)。 2. 绘制热图,并设置y轴标签为基因名称(假设基因名称在行索引中)。 3. 使用twinx()或通过指定位置创建一个新的坐标轴,共享y轴(但位置在右侧)。 4. 在右侧坐标轴绘制水平柱状图(barh),y位置与热图基因位置对齐。 但是,由于热图的y轴是离散的刻度(每个基因一个位置),我们需要确保柱状图的y位置与这些刻度一一对应。 另一种方法:使用GridSpec来布局,将右侧的柱状图放在热图的右侧,并共享y轴。 这里我们使用一个简单的方法:先绘制热图,然后在其右侧添加一个坐标轴,然后绘制柱状图,并设置相同的y轴刻度。 假设热图的y轴刻度位置可以通过`ax.get_yticks()`获取,刻度标签可以通过`ax.get_yticklabels()`获取。 然而,由于热图可能只显示部分基因(如果基因很多,标签可能会被隐藏),所以我们最好使用完整的基因列表(数据索引)来确保顺序。 步骤详解: 1. 读取热图数据(假设为DataFrame,行索引为基因名称)和柱状图数据(Excel文件,包含一列基因名称和一列数值)。 2. 确保柱状图数据按照热图数据的行索引(基因名称)的顺序排列。 3. 创建图形和第一个坐标轴(热图坐标轴)。 4. 使用sns.heatmap绘制热图,并设置ax为第一个坐标轴。 5. 在右侧添加一个共享y轴的坐标轴(但不共享x轴,并且位置在右侧)。 6. 在右侧坐标轴上绘制水平柱状图(barh),y值使用热图数据索引的长度(0到len(genes)-1)作为位置,宽度为柱状图数据值。 7. 设置右侧坐标轴的y轴刻度位置与热图一致,标签为空(因为已经有了热图的y轴标签),并设置x轴范围使其美观。 注意:由于热图的y轴是分类轴(每个刻度是一个类别),柱状图需要将每个条形绘制在对应的刻度位置。 具体代码结构: ```python import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import numpy as np # 1. 读取热图数据(假设为data_heatmap,行索引为基因名称) # data_heatmap = pd.read_csv(...) # 假设已经读取 # 2. 读取柱状图数据(Excel文件) bar_data = pd.read_excel('bar_data.xlsx') # 假设文件名为bar_data.xlsx # 假设bar_data有两列:'Gene'(基因名称)和'Value'(数值) # 3. 确保柱状图数据与热图数据的基因顺序一致 # 注意:热图数据data_heatmap的行索引(基因名称)顺序将决定热图的y轴顺序。 # 因此,我们需要将柱状图数据按照热图的行索引顺序排列。 # 将柱状图数据的索引设置为基因名称,然后按热图的行索引重排 bar_data = bar_data.set_index('Gene') bar_data = bar_data.loc[data_heatmap.index] # 按照热图的行索引顺序排序 # 4. 创建图形 fig = plt.figure(figsize=(12, 8)) # 调整宽度以容纳右侧柱状图 # 5. 绘制热图 ax_heatmap = fig.add_subplot(111) # 主坐标轴 sns.heatmap(data_heatmap, ax=ax_heatmap, cmap='viridis') # 绘制热图 # 6. 在右侧创建坐标轴(与热图共享y轴) ax_bar = ax_heatmap.twinx() # 共享y轴,但创建在右侧 # 7. 设置柱状图的y位置(0到len(genes)-1),因为热图的y轴刻度位置就是0,1,...,len(genes)-1(从顶部到底部) y_positions = np.arange(len(data_heatmap.index)) # 8. 绘制水平柱状图(注意:水平柱状图使用barh,参数y是位置,宽度是数值) # 这里我们使用barh,注意y_positions对应每个基因的位置,高度设为1(每个条带高度为1,与热图的每个行高度一致),宽度为bar_data['Value']的值 ax_bar.barh(y_positions, bar_data['Value'], height=1, align='center', color='blue') # 9. 调整柱状图坐标轴的范围,使其与热图对齐 # 设置y轴范围:热图的y轴范围是从0到基因数量(len(y_positions)),柱状图同样 ax_bar.set_ylim(ax_heatmap.get_ylim()) # 重要:共享y轴范围 # 10. 隐藏右侧坐标轴的y轴标签和刻度(因为已经和热图共享,所以刻度标签会重复,我们不需要) ax_bar.yaxis.set_visible(False) # 隐藏整个y轴(包括刻度和标签) # 11. 设置柱状图的x轴标签等 ax_bar.set_xlabel('Bar Data Value') # 12. 调整布局,确保柱状图在右侧且不重叠 plt.tight_layout() # 13. 显示图形 plt.show() ``` 但是,上述方法使用`twinx()`创建的坐标轴会与热图坐标轴重叠,并不会在右侧留出空间。因此,我们需要调整热图的位置,给右侧柱状图留出空间。 改进:使用GridSpec或subplots_adjust来调整热图的位置,在其右侧留出空白,然后在空白处绘制柱状图坐标轴。 更合理的方法: 1. 创建图形后,将整个图分为两部分:大部分区域给热图,小部分区域给柱状图。 2. 使用`plt.subplots`创建两个并排的坐标轴(但共享y轴),但这样需要自己调整热图的颜色条位置。 另一种常用方法:先绘制热图,然后调整图形右侧的空间,再在右侧空间添加坐标轴。 这里我们使用`make_axes_locatable`(来自mpl_toolkits.axes_grid1)来创建右侧的坐标轴。但也可以手动指定位置。 为了简单起见,我们使用手动调整并创建新坐标轴的方法: ```python # 创建图形和热图坐标轴 fig, ax_heatmap = plt.subplots(figsize=(12, 8)) # 绘制热图 sns.heatmap(data_heatmap, ax=ax_heatmap, cmap='viridis') # 获取热图坐标轴的边界框(bbox) bbox = ax_heatmap.get_position() # 设置热图坐标轴的新边界框:将宽度缩小为原来的80%,留20%给柱状图 ax_heatmap.set_position([bbox.x0, bbox.y0, bbox.width * 0.8, bbox.height]) # 在右侧80%的位置创建一个新的坐标轴(从x0=0.8开始,宽度0.2) ax_bar = fig.add_axes([bbox.x0 + bbox.width * 0.8, bbox.y0, bbox.width * 0.2, bbox.height]) # 然后在ax_bar上绘制柱状图(水平柱状图) y_positions = np.arange(len(data_heatmap.index)) # 绘制水平柱状图(注意:由于热图的行是从上到下编号(0在最上),所以柱状图也要从上到下,但barh默认从下到上,因此需要反转y轴) # 但是,热图的y轴方向是:索引0在最上面,所以柱状图也应该0在最上面。 ax_bar.barh(y_positions, bar_data['Value'], height=1, align='center', color='blue') # 设置柱状图的y轴范围与热图一致(热图的y轴范围是从0到len(genes)) ax_bar.set_ylim(0, len(y_positions)) # 注意:热图的y轴方向是向上的(即0在顶部),而barh默认也是从底部开始(即0在底部),所以我们看到的是反转的。 # 因此,我们需要反转柱状图的y轴方向,使其与热图一致: ax_bar.invert_yaxis() # 隐藏柱状图的y轴(刻度线和标签) ax_bar.yaxis.set_visible(False) # 设置柱状图的x轴标签 ax_bar.set_xlabel('Value') # 设置柱状图的x刻度位置(可选) # 还可以设置标题等 plt.show() ``` 但是,上面的方法中,柱状图的y轴反转后,位置与热图一致了吗?注意热图在绘制时默认也是反转y轴的(第一个行在顶部),所以我们绘制柱状图时也要让第一个基因在顶部。 然而,我们注意到:在热图中,sns.heatmap默认设置了`ax_heatmap.invert_yaxis()`(即反转y轴,使索引0在顶部)。而我们用`barh`绘制柱状图时,默认是0在底部,所以我们需要反转柱状图的y轴方向(`ax_bar.invert_yaxis()`),这样柱状图的顶部就是0(第一个基因),与热图保持一致。 但是,我们还需要确保柱状图的每个条带与热图的每一行对齐。因为我们使用相同的y_positions(0,1,...,n-1),并且设置了相同的y轴范围(0到n),并反转了y轴,所以应该是对齐的。 此外,由于热图在绘制时每个单元格的高度为1,所以我们的柱状图高度也设为1,这样每个条带高度正好是一个单元格的高度。 注意:如果热图的行数很多,柱状图的条带可能会重叠,但由于高度为1且位置是整数,所以不会重叠。 另一种方式:使用相同的y轴刻度位置。我们可以获取热图的y轴刻度位置(通过`ax_heatmap.get_yticks()`),然后设置柱状图的y轴刻度位置与之相同。但是,由于我们已经设置了相同的范围和反转,并且使用整数位置,所以刻度位置自然相同。 但是,当基因数量很多时,热图可能不会显示所有刻度标签(只显示部分),而我们隐藏了柱状图的y轴,所以不影响。 总结步骤: 1. 读取热图数据和柱状图数据,并按热图的行索引排序柱状图数据。 2. 创建图形和热图坐标轴。 3. 绘制热图,并调整热图坐标轴的位置(右侧留出20%空间)。 4. 在右侧创建柱状图坐标轴。 5. 在柱状图坐标轴上绘制水平柱状图,高度为1,y位置为np.arange(len(genes))。 6. 反转柱状图的y轴(使其与热图一致)。 7. 设置柱状图的y轴范围与热图相同(0到基因数量)。 8. 隐藏柱状图的y轴。 注意:如果柱状图数据中有负值,可能需要调整柱状图的x轴范围。 代码示例(整合): </think>我将为您介绍如何在热图右侧添加柱状图,并将柱状图左对齐到y轴基因名称。以下是完整的解决方案: ### 解决方案代码 ```python import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from matplotlib.gridspec import GridSpec # 1. 准备数据 # 假设gene_exp是热图数据(基因表达矩阵),bar_data是柱状图数据 gene_exp = pd.read_csv("gene_expression.csv", index_col=0) # 行索引为基因名称 bar_data = pd.read_excel("bar_data.xlsx", index_col=0) # 柱状图数据 # 2. 创建图形布局 fig = plt.figure(figsize=(15, 10)) gs = GridSpec(1, 2, width_ratios=[4, 1], wspace=0.05) # 4:1宽度比例 # 3. 绘制热图 ax_heatmap = fig.add_subplot(gs[0]) sns.heatmap(gene_exp, cmap="viridis", ax=ax_heatmap, cbar_kws={"location": "top", "pad": 0.05}) ax_heatmap.set_ylabel("基因名称") ax_heatmap.set_xlabel("样本") # 4. 绘制柱状图 ax_bar = fig.add_subplot(gs[1], sharey=ax_heatmap) plt.setp(ax_bar.get_yticklabels(), visible=False) # 隐藏y轴标签 # 确保柱状图数据顺序与热图一致 bar_data = bar_data.reindex(index=gene_exp.index) # 绘制水平柱状图(重要:使用barh实现水平方向) ax_bar.barh(range(len(gene_exp)), bar_data['Value'], height=1, align='edge', color='skyblue') # 5. 对齐设置 # 获取热图y轴基因位置 y_positions = np.arange(len(gene_exp)) + 0.5 ax_bar.set_yticks(y_positions) # 设置相同刻度位置 ax_bar.set_ylim(ax_heatmap.get_ylim()) # 同步y轴范围 # 6. 美化格式 ax_bar.set_xlabel('数值') ax_bar.spines['top'].set_visible(False) ax_bar.spines['right'].set_visible(False) ax_bar.grid(axis='x', linestyle='--', alpha=0.7) # 7. 调整布局 plt.tight_layout() plt.subplots_adjust(right=0.85) # 确保右侧无空白 plt.show() ``` ### 关键技术点说明 1. **布局控制**: - 使用`GridSpec`创建4:1的宽度比例布局 - `width_ratios=[4,1]`确保柱状图占总宽度20% - `wspace=0.05`消除两图之间的空白 2. **对齐实现**: - `sharey=ax_heatmap`确保共享y轴刻度 - 通过`set_yticks()`精确匹配基因位置 - `barh()`水平柱状图 + `align='edge'`实现左边界精确对齐 3. **数据处理**: - `reindex()`确保柱状图数据与热图基因顺序一致 - `np.arange(len(gene_exp))`生成精确的y轴位置索引 4. **可视化优化**: - 隐藏柱状图y轴标签避免重复 - 添加网格线提高可读性 - 调整边缘间距确保柱状图右边界对齐图形边缘 ### 示例数据结构 **gene_expression.csv** (热图数据): ``` 基因,样本1,样本2,样本3 GeneA,1.2,0.8,2.1 GeneB,0.5,1.2,0.9 GeneC,2.3,1.7,0.5 ``` **bar_data.xlsx** (柱状图数据): ``` 基因,Value GeneA,15 GeneB,8 GeneC,22 ``` ### 效果说明 最终生成的组合图中: 1. 左侧热图显示基因表达模式 2. 右侧柱状图左边界精确对齐基因名称 3. 柱状图右边界延伸至图形边缘 4. 柱状图高度与热图行高完美匹配 这种方法常用于展示基因表达量与其相关数值(如p值、fold-change等)的组合分析[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值