从病人分组到用户分群:利用二元变量相似度矩阵做聚类的完整流程(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_first=True) print(binary_df)输出结果:
患者ID 发烧_是 咳嗽_是 检测1_阳性 0 P001 1 0 1 1 P002 0 1 0 2 P003 1 0 12. 相似度矩阵构建的艺术
2.1 选择合适的相似度度量
相似度计算是聚类的核心,对于二元变量,最常用的两种方法是:
| 度量方法 | 公式 | 适用场景 | 特点 |
|---|---|---|---|
| 简单匹配系数 | (a+d)/(a+b+c+d) | 对称二元变量 | 考虑0-0匹配 |
| Jaccard系数 | a/(a+b+c) | 非对称二元变量 | 忽略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(f"Jaccard相似度: {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, metric='jaccard') 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, orientation='top', labels=['患者1', '患者2', '患者3'], distance_sort='descending') plt.title('患者症状层次聚类') plt.ylabel('距离') plt.show()3.2 DBSCAN聚类应用
当数据中存在噪声点或异常值时,DBSCAN是更好的选择:
from sklearn.cluster import DBSCAN # 将不相似度矩阵转换为距离矩阵 dbscan = DBSCAN(metric='precomputed', eps=0.7, min_samples=1) 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, metric='jaccard') # 进行层次聚类 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系数对罕见病患者的聚类效果明显优于欧氏距离。曾经有一个案例,通过调整相似度计算方法,成功识别出了一个被传统方法忽略的高风险患者亚群。
