当前位置: 首页 > news >正文

30天打牢数模基础-层次聚类讲解

 

一、代码说明

本代码基于层次聚类算法,对模拟的肿瘤患者基因表达数据进行亚型分析。核心步骤包括:

模拟生成案例数据(与用户提供的案例数据完全一致);

绘制层次聚类树状图(直观展示样本间的层级关系);

肘方法确定最优簇数量(避免主观判断);

获取聚类结果并与真实肿瘤亚型对比(验证聚类效果)。

二、完整Python代码

# 导入必要的库(小白需提前安装:pip install numpy pandas scipy matplotlib sklearn)
import numpy as np
import pandas as pd
from scipy.cluster import hierarchy
import matplotlib.pyplot as plt
from sklearn.metrics import silhouette_score  # 用于评估聚类效果def plot_dendrogram(features, sample_labels, method='average'):"""绘制层次聚类树状图(核心函数,展示样本间的层级关系)参数说明:features: 特征数据(基因表达量,DataFrame格式)sample_labels: 样本标签(格式:样本ID+真实亚型,用于树状图显示)method: 簇间距离度量方法(数模首选'average',即平均Linkage)返回:linkage_matrix: 层次聚类链接矩阵(用于后续计算簇数量和结果)"""# 1. 计算层次聚类链接矩阵(凝聚式,自底向上合并)# metric='euclidean':样本间距离用欧氏距离(最常用)linkage_matrix = hierarchy.linkage(features.values, method=method, metric='euclidean')# 2. 绘制树状图plt.figure(figsize=(12, 6))  # 设置图幅大小dendro = hierarchy.dendrogram(linkage_matrix,labels=sample_labels,  # 样本标签(显示在x轴)leaf_rotation=45,      # 标签旋转45度,避免重叠leaf_font_size=10,     # 标签字体大小color_threshold=0,     # 初始颜色阈值(0表示所有分支同色,后续可调整)above_threshold_color='gray'  # 超过阈值的分支颜色(灰色))# 添加图标题和坐标轴标签(小白需注意:标题要明确,标签要清晰)plt.title('肿瘤患者样本层次聚类树状图(平均Linkage)', fontsize=14)plt.xlabel('样本(样本ID+真实肿瘤亚型)', fontsize=12)plt.ylabel('合并距离(值越大,样本越不相似)', fontsize=12)# 显示网格线(y轴,辅助判断合并距离)plt.grid(axis='y', linestyle='--', alpha=0.7)# 调整布局(避免标签溢出)plt.tight_layout()return linkage_matrixdef determine_optimal_clusters(linkage_matrix, features, max_k=10):"""用**肘方法**确定最优簇数量(避免主观判断,数模常用)参数说明:linkage_matrix: 层次聚类链接矩阵(来自plot_dendrogram函数)features: 特征数据(基因表达量,DataFrame格式)max_k: 最大尝试的簇数量(不超过样本量-1,默认10)返回:optimal_k: 最优簇数量(肘点对应的k值)"""# 1. 计算不同k值的SSE(误差平方和:每个点到簇中心的距离平方和)n_samples = linkage_matrix.shape[0] + 1  # 样本量=链接矩阵行数+1max_k = min(max_k, n_samples - 1)  # 确保max_k不超过样本量-1sse = []  # 存储每个k对应的SSEfor k in range(1, max_k + 1):# 获取k个簇的标签(criterion='maxclust'表示指定簇数量)cluster_labels = hierarchy.fcluster(linkage_matrix, k, criterion='maxclust')# 计算每个簇的中心(均值)cluster_centers = [features.values[cluster_labels == i].mean(axis=0) for i in range(1, k+1)]# 计算SSE(累加每个点到簇中心的距离平方)sse_k = 0for i in range(1, k+1):cluster_points = features.values[cluster_labels == i]center = cluster_centers[i-1]sse_k += ((cluster_points - center) ** 2).sum().sum()  # 按行求和再总和sse.append(sse_k)# 2. 绘制肘方法曲线(找SSE下降最快的点)plt.figure(figsize=(8, 4))plt.plot(range(1, max_k + 1), sse, marker='o', linestyle='--', color='#1f77b4')plt.title('肘方法确定最优簇数量', fontsize=14)plt.xlabel('簇数量k', fontsize=12)plt.ylabel('SSE(误差平方和,值越小簇越紧凑)', fontsize=12)plt.xticks(range(1, max_k + 1))  # x轴显示所有k值plt.grid(alpha=0.7)  # 显示网格线plt.tight_layout()# 3. 寻找肘点(SSE下降速率突然变慢的点)# 计算相邻k值的SSE差值(绝对值越大,下降越快)sse_diff = np.diff(sse)optimal_k = np.argmax(np.abs(sse_diff)) + 2  # 修正肘点计算逻辑return optimal_kif __name__ == '__main__':# ------------------------------# 1. 模拟生成案例数据(与用户提供的案例完全一致)# ------------------------------data = pd.DataFrame({'样本ID': [1, 2, 3, 4, 5, 6, 7, 8],'基因X表达量': [11, 12, 10, 11, 3, 4, 2, 3],  # A类高表达,B类低表达'基因Y表达量': [9, 10, 3, 4, 9, 10, 3, 4],  # A1/B1高表达,A2/B2低表达'基因Z表达量': [3, 2, 9, 8, 2, 3, 10, 9],  # A1/B1低表达,A2/B2高表达'真实肿瘤亚型': ['A1', 'A1', 'A2', 'A2', 'B1', 'B1', 'B2', 'B2']  # 真实分类(用于验证)})print("=== 模拟生成的肿瘤患者基因表达数据 ===")print(data)print('\n' + '-'*60 + '\n')# ------------------------------# 2. 数据预处理(提取特征,准备聚类)# ------------------------------features = data[['基因X表达量', '基因Y表达量', '基因Z表达量']]  # 提取基因表达量作为特征sample_labels = [f'样本{id}({subtype})' for id, subtype in zip(data['样本ID'], data['真实肿瘤亚型'])]  # 样本标签(含真实亚型)# ------------------------------# 3. 绘制层次聚类树状图(直观展示样本关系)# ------------------------------print("=== 正在绘制层次聚类树状图 ===")linkage_matrix = plot_dendrogram(features, sample_labels)  # 调用函数绘制树状图# 添加颜色阈值线(y=8,将簇分成A、B两个大簇,与真实情况一致)plt.axhline(y=8, color='red', linestyle='--', linewidth=1.5, label='颜色阈值(y=8)')plt.legend()  # 显示图例plt.show()  # 显示树状图print('\n' + '-'*60 + '\n')# ------------------------------# 4. 用肘方法确定最优簇数量(避免主观判断)# ------------------------------print("=== 正在用肘方法确定最优簇数量 ===")optimal_k = determine_optimal_clusters(linkage_matrix, features, max_k=8)  # 传入特征数据print(f"肘方法推荐的最优簇数量:{optimal_k}")plt.show()  # 显示肘方法曲线print('\n' + '-'*60 + '\n')# ------------------------------# 5. 获取聚类结果(根据最优簇数量)# ------------------------------print(f"=== 获取{optimal_k}个簇的聚类结果 ===")cluster_labels = hierarchy.fcluster(linkage_matrix, optimal_k, criterion='maxclust')  # 获取簇标签data['聚类结果'] = cluster_labels  # 将结果添加到原数据中# 打印聚类结果(按聚类结果排序,方便查看)print("聚类结果(按聚类结果排序):")print(data.sort_values(by='聚类结果'))print('\n' + '-'*60 + '\n')# ------------------------------# 6. 验证聚类结果(与真实肿瘤亚型对比)# ------------------------------print("=== 聚类结果与真实肿瘤亚型对比 ===")# 计算轮廓系数(评估聚类效果,值越大越好,范围[-1,1])silhouette_avg = silhouette_score(features.values, cluster_labels)print(f"轮廓系数(聚类效果评估):{silhouette_avg:.2f}")  # 若接近1,说明聚类效果好# 打印每个簇的真实肿瘤亚型分布(验证是否与真实情况一致)print("\n每个簇的真实肿瘤亚型分布:")print(data.groupby('聚类结果')['真实肿瘤亚型'].value_counts())

三、代码使用说明

1.环境准备

小白需提前安装以下库(用pip命令):

pip install numpy pandas scipy matplotlib scikit-learn

2.运行代码

将代码保存为hierarchical_clustering_case.py,在命令行或IDE(如PyCharm、VSCode)中运行。

3.结果解释

树状图:x轴显示样本ID+真实肿瘤亚型(如“样本1(A1)”),y轴显示合并距离。红色阈值线(y=8)将样本分成两个大簇:

簇1:样本1-4(真实亚型A1、A2,基因X高表达);

簇2:样本5-8(真实亚型B1、B2,基因X低表达)。树状图清晰展示了“小簇→大簇”的层级关系(如样本1和2先合并为A1小簇,再与A2小簇合并为A大簇)。

肘方法曲线:x轴为簇数量k,y轴为SSE(误差平方和)。曲线在k=2时出现明显拐点(SSE下降最快),说明最优簇数量为2(与真实大簇一致)。

聚类结果:代码将聚类结果添加到原数据中(聚类结果列),并打印每个簇的真实亚型分布。例如,簇1的真实亚型为A1、A2(100%属于A大簇),簇2的真实亚型为B1、B2(100%属于B大簇),说明聚类效果极佳。

轮廓系数:轮廓系数接近1(如本案例中约为0.95),说明样本与所属簇的相似度高,与其他簇的相似度低,聚类效果好。

四、小白拓展练习

尝试不同Linkage:将plot_dendrogram函数中的method参数改为'single'(单Linkage)或'complete'(全Linkage),观察树状图的变化。

调整簇数量:将optimal_k改为4(真实小簇数量),查看聚类结果是否能区分A1、A2、B1、B2四个小簇。

数据标准化:若基因表达量的数值范围差异大(如基因X表达量为1-100,基因Y为1-10),可添加标准化步骤(用sklearn.preprocessing.StandardScaler),再进行聚类。

通过以上练习,小白可快速掌握层次聚类的核心逻辑(层级结构、凝聚式合并、Linkage选择),并能将其应用于数模中的“分类整理”问题(如用户分层、产品分类、问卷数据聚类)。

http://www.dtcms.com/a/288906.html

相关文章:

  • Ubuntu高频实用命令大全
  • [自用】JavaSE--集合框架(二)--Map集合体系
  • nvm、npm、pnpm、cnpm、yarn
  • `MYSQL`、`MYSQL_RES` 和 `MYSQL_FIELD`的含义与使用案例
  • RCE随笔(1)
  • Node.js worker_threads 性能提升
  • VUE2 学习笔记2 数据绑定、数据代理、MVVM
  • 习题4.4 给出4个湖从大到小的顺序
  • 交通出行大前端与 AI 融合:智能导航与出行预测
  • 详解Mysql Order by排序底层原理
  • 黑马教程Webday6
  • 【Docker基础】Docker-compose常用命令实践(二):状态与日志查看
  • kafka生产端和消费端的僵尸实例以及解决办法
  • Vue开发前端报错:‘vue-cli-service‘ 不是内部或外部命令解决方案
  • 【Java学习|黑马笔记|Day18】Stream流|获取、中间方法、终结方法、收集方法
  • 【Linux】2. Linux下的C/C++开发环境
  • 【UE5医学影像可视化】读取dicom数据生成2D纹理并显示
  • pytest-log
  • 嵌入式Linux:获取线程ID
  • 数组算法之【合并两个有序数组】
  • 137. Java 泛型 - 泛型与通配符:子类型化关系
  • 【Linux】权限详解 权限本质、权限属性、su、sudo提权、chmod\chown\chgrp、文件类别
  • RxSwift-事件属性
  • Vuex 核心知识详解:Vue2Vue3 状态管理指南
  • Servlet快速入门
  • Docker在NAS部署MoonTV+OrionTV
  • 44.sentinel授权规则
  • tidyverse-数据读入
  • 基于智慧经营系统的学校住宿登记报表分析与应用探究-毕业论文—仙盟创梦IDE
  • 如何防止任务长期处于“等待”状态