从病人分组到用户分群:利用二元变量相似度矩阵做聚类的完整流程(Sklearn实战)
从病人分组到用户分群二元变量相似度矩阵的聚类实战指南在医疗健康领域医生常常需要根据患者的症状、检查结果等特征将病人分成不同的亚组以便制定个性化的治疗方案。同样在商业分析中市场人员也希望通过用户的购买行为、浏览记录等特征进行客户细分。这两种看似不同的场景背后都依赖于同一种核心技术——基于二元变量的聚类分析。本文将带您深入探索如何利用Python的Sklearn库从原始二元数据出发构建样本间相似度矩阵最终实现有业务意义的群体划分。不同于普通的教程我们会重点讨论在实际业务场景中如何选择相似度计算方法Jaccard系数vs简单匹配系数以及如何解读聚类结果。无论您是医疗领域的研究人员还是商业数据分析师都能从中获得可直接复用的代码和思路。1. 二元变量聚类的基础准备1.1 理解二元变量特性二元变量Binary Variable是数据挖掘中最基础却又最容易被误用的数据类型之一。它只有两个取值1表示存在或是和0表示不存在或否。在实际应用中二元变量可以表示医疗场景患者是否发烧1/0、检测结果是否阳性1/0电商场景用户是否购买过某类商品1/0、是否点击过广告1/0关键区别在于对称与非对称二元变量# 对称二元变量示例性别男女权重相同 symmetrical_vars [性别_男, 性别_女] # 非对称二元变量示例罕见病诊断得病1更重要 asymmetrical_vars [糖尿病, 高血压]1.2 数据预处理实战原始数据通常需要转换为适合聚类的二元矩阵形式。以下是一个典型的数据转换过程import pandas as pd from sklearn.preprocessing import LabelEncoder # 原始患者数据示例 data { 患者ID: [P001, P002, P003], 发烧: [是, 否, 是], 咳嗽: [否, 是, 否], 检测1: [阳性, 阴性, 阳性] } df pd.DataFrame(data) # 转换为二元变量 binary_df pd.get_dummies(df, columns[发烧, 咳嗽, 检测1], drop_firstTrue) print(binary_df)输出结果患者ID 发烧_是 咳嗽_是 检测1_阳性 0 P001 1 0 1 1 P002 0 1 0 2 P003 1 0 12. 相似度矩阵构建的艺术2.1 选择合适的相似度度量相似度计算是聚类的核心对于二元变量最常用的两种方法是度量方法公式适用场景特点简单匹配系数(ad)/(abcd)对称二元变量考虑0-0匹配Jaccard系数a/(abc)非对称二元变量忽略0-0匹配其中a两个样本都为1的特征数b样本i为1而j为0的特征数c样本i为0而j为1的特征数d两个样本都为0的特征数医疗场景示例from sklearn.metrics import jaccard_score # 患者症状数据 p1 [1, 0, 1] # 发烧是, 咳嗽否, 检测阳性 p2 [1, 1, 0] # 发烧是, 咳嗽是, 检测阴性 # 计算Jaccard相似度适用于非对称变量如疾病诊断 similarity jaccard_score(p1, p2) print(fJaccard相似度: {similarity:.2f})2.2 构建完整的相似度矩阵实际分析中我们需要计算所有样本两两之间的相似度from sklearn.metrics.pairwise import pairwise_distances import numpy as np # 示例患者数据矩阵行代表患者列代表症状/检测结果 patients np.array([ [1, 0, 1], # 患者1 [1, 1, 0], # 患者2 [0, 1, 0] # 患者3 ]) # 计算不相似度矩阵1 - Jaccard相似度 dissimilarity_matrix pairwise_distances(patients, metricjaccard) print(患者间不相似度矩阵:) print(dissimilarity_matrix)输出结果患者间不相似度矩阵: [[0. 0.66666667 1. ] [0.66666667 0. 0.5 ] [1. 0.5 0. ]]3. 聚类算法选择与实施3.1 层次聚类实战层次聚类特别适合探索性分析可以直观展示样本间的分组关系from scipy.cluster.hierarchy import dendrogram, linkage import matplotlib.pyplot as plt # 使用上节计算的相似度矩阵 linked linkage(dissimilarity_matrix, ward) plt.figure(figsize(10, 6)) dendrogram(linked, orientationtop, labels[患者1, 患者2, 患者3], distance_sortdescending) plt.title(患者症状层次聚类) plt.ylabel(距离) plt.show()3.2 DBSCAN聚类应用当数据中存在噪声点或异常值时DBSCAN是更好的选择from sklearn.cluster import DBSCAN # 将不相似度矩阵转换为距离矩阵 dbscan DBSCAN(metricprecomputed, eps0.7, min_samples1) clusters dbscan.fit_predict(dissimilarity_matrix) print(f聚类结果: {clusters})4. 聚类结果的业务解读4.1 医疗场景分析假设我们得到以下患者分群群组患者特征临床意义群组1高烧检测阳性可能为病毒感染群组2咳嗽不发烧可能为过敏或轻度呼吸道感染群组3无症状健康人群或潜伏期关键诊断指标对比# 计算各群组的特征平均值 group_features pd.DataFrame({ 群组: clusters, 发烧率: patients[:,0], 咳嗽率: patients[:,1], 阳性率: patients[:,2] }).groupby(群组).mean() print(group_features)4.2 商业场景应用同样的方法可以应用于用户分群# 电商用户行为聚类示例 user_behavior np.array([ [1,0,1,0], # 用户1购买A、未购买B、点击广告、未收藏 [1,1,0,1], # 用户2 [0,0,1,0] # 用户3 ]) # 计算Jaccard距离矩阵 user_dist pairwise_distances(user_behavior, metricjaccard) # 进行层次聚类 user_clusters linkage(user_dist, average)5. 进阶技巧与常见问题5.1 混合变量类型处理实际数据往往包含多种变量类型可以采用以下策略将连续变量分箱为二元变量使用Gower距离等混合度量方法分别计算后加权组合# 连续变量分箱示例 age [25, 30, 45, 60] age_binary pd.get_dummies(pd.cut(age, bins[0,30,50,100]))5.2 聚类质量评估虽然没有绝对标准但可以通过以下方法评估轮廓系数观察树状图切割高度业务合理性检查from sklearn.metrics import silhouette_score # 计算轮廓系数需要转换为相似度 silhouette_avg silhouette_score(1-dissimilarity_matrix, clusters) print(f轮廓系数: {silhouette_avg:.3f})在实际医疗数据分析项目中我们发现Jaccard系数对罕见病患者的聚类效果明显优于欧氏距离。曾经有一个案例通过调整相似度计算方法成功识别出了一个被传统方法忽略的高风险患者亚群。