基于主要祖先重命名聚类:pandas.DataFrame聚类层级调整需求
聚类层级分裂后的重命名与可视化实现方案
一、聚类编号重命名(基于子簇占比继承祖先名称)
核心逻辑
- 按分辨率从低到高遍历(如r1→r2→r3),低分辨率簇为高分辨率簇的父簇
- 对每个父簇,统计其在高分辨率下所有子簇的样本占比
- 占比最高的子簇继承父簇原始编号,其余子簇分配未使用的新编号
代码实现
假设你的DataFrame包含样本ID列与不同分辨率的聚类结果列(如r1、r2、r3):
import pandas as pd import numpy as np # 模拟示例数据 data = { "sample_id": range(1, 21), "r1": [1]*10 + [2]*10, "r2": [1]*6 + [3]*4 + [2]*10, "r3": [1]*6 + [3]*4 + [2]*7 + [1]*3 } df = pd.DataFrame(data) def rename_clusters_by_size(df, resolution_cols): # 按分辨率从低到高排序 resolution_cols_sorted = sorted(resolution_cols, key=lambda x: int(x[1:])) renamed_df = df.copy() # 记录已使用的聚类编号,避免冲突 used_ids = set(renamed_df[resolution_cols_sorted[0]].unique()) for i in range(len(resolution_cols_sorted)-1): parent_col = resolution_cols_sorted[i] child_col = resolution_cols_sorted[i+1] for parent_id in renamed_df[parent_col].unique(): # 筛选父簇对应的所有子簇样本 child_samples = renamed_df[renamed_df[parent_col] == parent_id] child_sizes = child_samples[child_col].value_counts() if len(child_sizes) <= 1: continue # 无分裂,无需调整 # 找到占比最高的子簇 largest_child_id = child_sizes.idxmax() # 替换最大子簇编号为父簇编号 if largest_child_id != parent_id: renamed_df.loc[ (renamed_df[parent_col] == parent_id) & (renamed_df[child_col] == largest_child_id), child_col ] = parent_id # 为剩余子簇分配新编号 other_child_ids = child_sizes.index[child_sizes.index != largest_child_id] for old_id in other_child_ids: new_id = max(used_ids) + 1 if used_ids else 1 while new_id in used_ids: new_id += 1 renamed_df.loc[ (renamed_df[parent_col] == parent_id) & (renamed_df[child_col] == old_id), child_col ] = new_id used_ids.add(new_id) used_ids.add(parent_id) return renamed_df # 执行重命名 resolution_cols = ["r1", "r2", "r3"] renamed_df = rename_clusters_by_size(df, resolution_cols) print(renamed_df)
二、用NetworkX绘制聚类层级分裂关系
核心逻辑
- 构建父簇与子簇的映射关系,记录每个分裂事件的样本占比
- 用有向层级图展示分裂结构,节点大小对应簇的样本量
代码实现
import networkx as nx import matplotlib.pyplot as plt def build_cluster_hierarchy(df, resolution_cols): resolution_cols_sorted = sorted(resolution_cols, key=lambda x: int(x[1:])) G = nx.DiGraph() # 添加最低分辨率的节点 first_col = resolution_cols_sorted[0] for cluster_id in df[first_col].unique(): size = len(df[df[first_col] == cluster_id]) G.add_node(f"{first_col}_{cluster_id}", size=size, label=f"{first_col}簇{cluster_id}\n({size}样本)") # 遍历构建层级连接 for i in range(len(resolution_cols_sorted)-1): parent_col = resolution_cols_sorted[i] child_col = resolution_cols_sorted[i+1] for parent_id in df[parent_col].unique(): parent_node = f"{parent_col}_{parent_id}" child_samples = df[df[parent_col] == parent_id] child_sizes = child_samples[child_col].value_counts() # 添加子节点并建立边连接 for child_id, size in child_sizes.items(): child_node = f"{child_col}_{child_id}" ratio = round(size / len(child_samples), 2) G.add_node(child_node, size=size, label=f"{child_col}簇{child_id}\n({size}样本, {ratio*100}%)") G.add_edge(parent_node, child_node, weight=ratio) return G # 构建层级图并绘制 G = build_cluster_hierarchy(renamed_df, resolution_cols) plt.figure(figsize=(12, 8)) # 节点大小按样本量缩放 node_sizes = [data["size"] * 50 for _, data in G.nodes(data=True)] node_labels = {node: data["label"] for node, data in G.nodes(data=True)} edge_labels = {(u, v): f"占比{data['weight']*100}%" for u, v, data in G.edges(data=True)} # 使用DOT布局生成层级结构 pos = nx.nx_agraph.graphviz_layout(G, prog="dot") nx.draw(G, pos, node_size=node_sizes, node_color="lightblue", with_labels=False, arrows=True) nx.draw_networkx_labels(G, pos, node_labels, font_size=10) nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=8) plt.title("聚类层级分裂关系图") plt.show()
内容的提问来源于stack exchange,提问作者ython33
相关产品推荐
相关产品推荐

