聚类分析引入
问题承接
在昨日的笔记中,我们深入探讨了运用聚类方法自动且合理划分群组的相关内容。聚类作为数据挖掘中一种重要的无监督学习方法,能够将数据集中相似的数据点划分到同一个群组中。我们详细研究了 KMeans 算法,它是一种基于距离度量的聚类算法,通过迭代的方式不断调整聚类中心,使得同一簇内的数据点距离尽可能小,不同簇之间的数据点距离尽可能大。同时,还学习了轮廓系数、肘部法则、CH 指数、DB 指数等评估聚类效果的指标。轮廓系数综合考虑了样本与自身簇的紧密程度以及与其他簇的分离程度;肘部法则通过观察不同簇数下的聚类误差平方和的变化,找到误差平方和下降趋势变缓的“肘部”,从而确定合适的簇数;CH 指数衡量了簇间离散程度与簇内离散程度的比值;DB 指数则反映了簇间相似度的最大值。我们还列出了一个表格,对各种方法之间的优缺点进行了综合比较,以便在实际应用中能够根据具体情况选择最合适的聚类方法和评估指标。
然而,在完成聚类操作后,我们面临着一个新的问题。虽然我们已经将数据点划分到了不同的群组中,每个群组都有一个编号,例如“1”“2”等,但我们并不知道这些群组背后所代表的实际含义。在实际应用中,仅仅知道数据点被划分到了哪些群组是远远不够的,我们更需要了解每个群组所对应的具体特征和业务意义,这样才能为后续的决策提供有价值的信息。因此,如何确定这些聚类后群组的实际含义,成为了我们今天需要解决的关键问题。
核心思路转换
问题转化
为了更好地理解如何确定聚类后群组的含义,我们以信贷数据集为例进行说明。在这个数据集中,我们使用 KMeans 算法对客户数据进行了聚类,得到了每个客户所属的群组“标签”,这些标签可能是 0、1 或 2 。但是,我们并不知道这些群组分别代表什么类型的客户群体。
此时,我们可以换一种思路,将原本的无监督聚类问题转换为监督学习的分类问题。具体来说,我们将客户的原始数据(如年龄、收入、负债等)作为特征 X ,将用 KMeans 算法计算出来的群组标签(0, 1, 2)作为目标 y 。这样,我们就构建了一个分类问题的数据集,其中特征 X 是输入,目标 y 是输出。
目的阐释
我们训练一个分类模型,例如随机森林分类器,其目的并不是为了得到一个能够完美预测群组标签的模型。因为在这个问题中,我们并不关心模型的预测准确率,而是关注模型在训练过程中所产生的一个副产品——特征重要性 (Feature Importance) 。
随机森林是一种集成学习方法,它由多个决策树组成。在训练过程中,每个决策树都会根据输入的特征进行决策,最终通过投票的方式确定输出结果。在这个过程中,随机森林模型会自动计算每个特征在决策过程中的重要性。如果模型在做决策时,最依赖某个特征,例如“Credit Score”(信用分),那么就意味着不同群组之间在这个特征上的差异最大,这个特征是区分这些群组的核心线索。通过分析特征重要性,我们就可以了解到哪些特征对于区分不同的群组起到了关键作用,从而推断出每个群组所代表的实际含义。
代码实现步骤
KMeans 聚类
# 提示用户选择 k 值
selected_k = 3 # 这里选择3后面好分析,也可以根据图选择最佳的k值
# 使用选择的 k 值进行 KMeans 聚类
kmeans = KMeans(n_clusters=selected_k, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)
X['KMeans_Cluster'] = kmeans_labels
# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# KMeans 聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=kmeans_labels, palette='viridis')
plt.title(f'KMeans Clustering with k={selected_k} (PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()
# 打印 KMeans 聚类标签的前几行
print(f"KMeans Cluster labels (k={selected_k}) added to X:")
print(X[['KMeans_Cluster']].value_counts())
在这段代码中,首先我们需要选择一个合适的簇数 k 。这里我们选择 k = 3 ,主要是为了方便后续的分析,但在实际应用中,我们也可以根据肘部法则等方法,通过观察不同 k 值下的聚类误差平方和的变化,选择最佳的 k 值。
然后,我们使用 KMeans 算法对经过标准化处理的数据 X_scaled 进行聚类。 fit_predict 方法会同时完成模型的训练和预测过程,得到每个数据点所属的群组标签 kmeans_labels 。接着,我们将这些标签添加到原始数据表 X 中,新增了一列“KMeans_Cluster”,这是本步骤最重要的操作。通过这一操作,我们的数据表同时包含了客户的“个人信息”(特征)和他们所属的“神秘团体”(标签),为后续的分类模型训练提供了必要的数据。
为了更直观地观察聚类结果,我们使用主成分分析(PCA)将高维数据降维到二维空间。PCA 是一种常用的数据降维方法,它能够在保留数据主要信息的前提下,将高维数据转换为低维数据,方便我们进行可视化展示。我们将降维后的数据 X_pca 绘制散点图,不同颜色的点代表不同的群组,这样我们就可以直观地看到数据点的分布情况。
最后,我们打印出 KMeans 聚类标签的前几行以及每个群组的样本数量,以便对聚类结果有一个初步的了解。
随机森林训练
x1 = X.drop('KMeans_Cluster', axis=1) # 删除聚类标签列
y1 = X['KMeans_Cluster']
# 构建随机森林,用 shap 重要性来筛选重要性
import shap
import numpy as np
from sklearn.ensemble import RandomForestClassifier # 随机森林分类器
model = RandomForestClassifier(n_estimators=100, random_state=42) # 随机森林模型
model.fit(x1, y1) # 训练模型,此时无需在意准确率 直接全部数据用来训练了
在进行随机森林模型训练之前,我们需要对数据进行预处理。由于我们要将聚类问题转换为分类问题,所以需要将原始数据表 X 中的聚类标签列“KMeans_Cluster”删除,得到特征矩阵 x1 ,同时将聚类标签列作为目标向量 y1 。
这里我们利用了 pandas 库中 DataFrame 的 drop 方法的特性,它可以方便地删除指定列,生成一个新的 DataFrame 。这样做的目的是为了防止在训练随机森林分类器时,模型直接利用聚类标签进行预测,从而导致分类器“作弊”,影响特征重要性的计算结果。
接下来,我们使用 sklearn 库中的 RandomForestClassifier 类构建随机森林模型。随机森林是一种强大的分类算法,它由多个决策树组成,通过集成多个决策树的预测结果,可以提高模型的准确性和稳定性。我们设置了 100 个决策树( n_estimators = 100 ),并指定了随机种子( random_state = 42 ),以确保模型的可重复性。
在训练模型时,我们不需要过于关注模型的准确率。因为我们的主要目的是通过模型计算特征的重要性,而不是进行精确的预测。所以,我们直接使用全部数据进行训练,而不进行数据的划分(如训练集和测试集的划分)。
SHAP 特征分析
shap.initjs()
# 初始化 SHAP 解释器
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(x1) # 这个计算耗时
shap_values.shape # 第一维是样本数,第二维是特征数,第三维是类别数
print("--- 1. SHAP 特征重要性条形图 ---")
shap.summary_plot(shap_values[:, :, 0], x1, plot_type="bar", show=False) # 这里的show=False表示不直接显示图形,这样可以继续用plt来修改元素,不然就直接输出了
plt.title("SHAP Feature Importance (Bar Plot)")
plt.show()
为了分析随机森林模型中各个特征的重要性,我们使用了 SHAP(SHapley Additive exPlanations)方法。SHAP 是一种基于博弈论的方法,它能够解释模型的预测结果,为每个特征分配一个重要性得分。
首先,我们需要初始化 SHAP 解释器。对于随机森林模型,我们使用 TreeExplainer 类来初始化解释器。然后,我们计算每个样本的 SHAP 值,这是一个比较耗时的过程,因为需要对每个样本和每个特征进行计算。SHAP 值的形状是一个三维数组,第一维表示样本数,第二维表示特征数,第三维表示类别数。
接下来,我们使用 shap.summary_plot 函数绘制 SHAP 特征重要性条形图。条形图的长度表示特征的重要性,长度越长,说明该特征在模型决策过程中起到的作用越大。我们将 show 参数设置为 False ,这样图形不会直接显示,我们可以使用 matplotlib 库的 plt 函数对图形进行进一步的修改,例如添加标题等,最后再显示图形。
特征类型判断
import pandas as pd
selected_features = ['Purpose_debt consolidation', 'Bankruptcies',
'Number of Credit Problems', 'Purpose_other']
for feature in selected_features:
unique_count = X[feature].nunique() # 唯一值指的是在某一列或某个特征中,不重复出现的值
# 连续型变量通常有很多唯一值,而离散型变量的唯一值较少
print(f'{feature} 的唯一值数量: {unique_count}')
if unique_count < 10: # 这里 10 是一个经验阈值,可以根据实际情况调整
print(f'{feature} 可能是离散型变量')
else:
print(f'{feature} 可能是连续型变量')
在分析出特征的重要性排名后,我们需要判断这些特征是离散型变量还是连续型变量。这是因为不同类型的特征在后续的分析中需要采用不同的方法。
我们选择了几个重要的特征,存储在 selected_features 列表中。对于每个特征,我们使用 pandas 库的 nunique 方法计算其唯一值的数量。唯一值是指在某一列或某个特征中,不重复出现的值。一般来说,连续型变量通常具有很多唯一值,因为它们可以取任意实数值;而离散型变量的唯一值数量相对较少,因为它们只能取有限个或可数个值。
我们设定了一个经验阈值 10 ,如果某个特征的唯一值数量小于 10 ,我们就认为该特征可能是离散型变量;否则,我们认为该特征可能是连续型变量。需要注意的是,这个阈值并不是绝对的,在实际应用中,我们可以根据具体情况进行调整。
簇含义确定
重要特征分布
# 总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten()
for i, feature in enumerate(selected_features):
axes[i].hist(X[feature], bins=20)
axes[i].set_title(f'Histogram of {feature}')
axes[i].set_xlabel(feature)
axes[i].set_ylabel('Frequency')
plt.tight_layout()
plt.show()
# 先绘制簇0的分布图
import matplotlib.pyplot as plt
# 总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten()
for i, feature in enumerate(selected_features):
axes[i].hist(X_cluster0[feature], bins=20)
axes[i].set_title(f'Histogram of {feature}')
axes[i].set_xlabel(feature)
axes[i].set_ylabel('Frequency')
plt.tight_layout()
plt.show()
为了确定每个簇背后所代表的具体含义,我们需要分析每个簇中重要特征的分布情况。我们使用直方图来展示特征的分布,直方图能够直观地显示数据的分布频率。
首先,我们绘制总样本中前四个重要特征的直方图。我们使用 matplotlib 库的 subplots 函数创建一个 2x2 的子图布局,将四个特征的直方图分别绘制在不同的子图中。每个子图的标题显示特征的名称,x 轴表示特征的值,y 轴表示频率。通过观察总样本的特征分布,我们可以对数据的整体情况有一个初步的了解。
然后,我们分别绘制每个簇中重要特征的直方图。以簇 0 为例,我们从数据集中筛选出属于簇 0 的样本,存储在 X_cluster0 中,然后绘制这些样本中前四个重要特征的直方图。通过比较不同簇中特征的分布情况,我们可以发现每个簇的特征差异,从而推断出每个簇所代表的具体含义。例如,如果某个簇中“Credit Score”(信用分)的分布集中在较高的值,那么这个簇可能代表信用良好的客户群体;如果另一个簇中“Number of Credit Problems”(信用问题数量)的分布集中在较高的值,那么这个簇可能代表信用较差的客户群体。
知识点回顾
- 推断簇含义的 2 个思路:先选特征和后选特征。先选特征是指在聚类之前,根据业务知识或经验选择一些可能重要的特征,然后基于这些特征进行聚类;后选特征则是在聚类之后,通过分析聚类结果来确定哪些特征对于区分不同的簇起到了关键作用。
- 通过可视化图形借助 AI 定义簇的含义:可视化图形可以帮助我们更直观地观察数据的分布情况,而 AI 技术(如随机森林、SHAP 等)可以帮助我们分析特征的重要性,从而为确定簇的含义提供有力的支持。
- 科研逻辑闭环:通过精度判断特征工程价值。在进行特征工程时,我们需要不断地评估特征对模型性能的影响。如果通过添加或筛选特征,模型的精度得到了提高,那么说明这些特征是有价值的;反之,如果模型精度没有明显变化或下降,那么我们需要重新考虑特征工程的方法和策略。
1918

被折叠的 条评论
为什么被折叠?



