You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于主要祖先重命名聚类: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 19:05:24