python打卡day18

发布于:2025-05-09 ⋅ 阅读:(20) ⋅ 点赞:(0)

聚类后的分析:推断簇的类型

知识点回顾:

  1. 推断簇含义的2个思路:先选特征和后选特征
  2. 通过可视化图形借助ai定义簇的含义
  3. 科研逻辑闭环:通过精度判断特征工程价值

作业:参考示例代码对心脏病数据集采取类似操作,并且评估特征工程后模型效果有无提升。

昨天的内容只是使用三种聚类算法完成了聚类以及结果可视化,以KMeans算法K = 6为例:

对于聚类后形成的六个簇没有做论述,现在需要给这个簇赋予实际的含义,一般当你赋予实际含义的时候,你需要根据某几个特征来赋予,但是源数据特征很多,如何选择特征呢?有2种思路:

1. 你最开始聚类的时候,就选择了你想最后用来确定簇含义的特征,那么你需要选择一些特征来进行聚类,那么你最后确定簇含义的特征就是这几个特征,而非全部。如你想聚类消费者购买习惯,那么他过去的消费记录、购买记录、购买金额等等,这些特征都与消费者购买习惯有关,你可以使用这些特征来确定簇含义,一些其他的特征,如消费者年龄,工作行业则不考虑。------适用于你本身就有构造某些明确含义的特征的情况。

2. 最开始用全部特征来聚类,把其余特征作为 x,聚类得到的簇类别作为标签构建监督模型,进而根据重要性筛选特征,来确定要根据哪些特征赋予含义。------使用于你想构造什么,目前还不清楚。

总的来说要么人为选取重要特征再进行聚类;要么全部特征聚类完后,把簇类别当标签转换成分类问题,用shap值等方式显示重要特征,这里选择思路二,选择随机森林分类模型进行操作

import shap
import numpy as np
from sklearn.ensemble import RandomForestClassifier  # 随机森林分类器

# 构建随机森林分类模型,用shap重要性来筛选重要性
x1= X.drop('KMeans_Cluster',axis=1) # 聚类的时候把聚类结果添加到原数据里了,删除聚类标签列其余当变量
y1 = X['KMeans_Cluster']
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)  # 随机森林模型
rf_model.fit(x1, y1)  # 训练模型,此时无需在意准确率 直接全部数据用来训练了

shap.initjs() # 必须在绘制交互图前调用
# 初始化 SHAP 解释器
explainer = shap.TreeExplainer(rf_model)
shap_values = explainer.shap_values(x1) # 这个计算耗时
shap_values.shape # 第一维是样本数,第二维是特征数,第三维是类别数,结果是(7500, 31, 6)

# ---  SHAP 特征重要性条形图 (Summary Plot - Bar) ---
shap.summary_plot(shap_values[:, :, 0], x1, plot_type="bar",show=False)  #  这里的show=False表示不直接显示图形,这样可以继续用plt来修改元素,不然就直接输出了
plt.title("SHAP Feature Importance (Bar Plot)")
plt.show()

很明显前四个特征对于聚类结果比较重要,为了对特征针对性可视化和正确解释聚类结果,下面要区分是连续变量还是离散变量(之前对于这个数据集连续和离散的处理是根据数据类型来的,这里都是数值型,要用唯一值细分一下了)

# 通过唯一值看看是连续还是离散
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} 可能是连续型变量')


# ---------- 打印结果 ------------
Purpose_debt consolidation 的唯一值数量: 2
Purpose_debt consolidation 可能是离散型变量
Bankruptcies 的唯一值数量: 5
Bankruptcies 可能是离散型变量
Number of Credit Problems 的唯一值数量: 8
Number of Credit Problems 可能是离散型变量
Purpose_other 的唯一值数量: 2
Purpose_other 可能是离散型变量

那都按离散变量的可视化来仔细看看,画直方图

# 总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten() # 将二维的 axes 数组转换为一维数组(方便用单个索引遍历)

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()

这几个重要特征是找出来了,但没有和簇的分类对应上啊,每个簇和几个特征有关呢?(这里说一下,是要筛选每个簇对应的这四个特征所有行数据,然后画直方图,这个筛选行数据的方法可以注意一下,第一次接触)

clusters = {} # 创建一个字典
for i in range(6):  # 6个簇,循环筛出每簇的所有行
    clusters[f'cluster{i}'] = X[X['KMeans_Cluster'] == i] 

# 在簇0中总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten()

for i, feature in enumerate(selected_features):
    axes[i].hist(clusters['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()

这里只画了簇0的,分别画出6个簇的四个重要特征直方图,看不明白很正常,丢给大模型问问,让它总结定义每个簇的含义,比如:

  • 簇 0 代表了在债务合并用途上表现一致,几乎无破产记录,信用问题极少,资金用途集中且很少涉及特殊类别,财务状况稳定,信用良好,资金流向明确的群体
  • 簇 1 代表了多数无债务合并需求,破产情况少见,但信用问题上存在个体差异,资金用途有一定分散性。整体财务状况相对稳定,但在信用和资金使用方向上不如第一个簇表现一致

好了,通过聚类对每个簇进行定义后,簇标签可视为数据的新特征,且这一过程属于特征工程,后续研究需要对这个特征独热编码,然后重新建模训练,如果加了这个特征后模型精度提高,说明这个特征是有用的

收获心得:

这里再辨析一下单括号和双中括号访问,之前没搞清楚,推荐用双中括号不仅能保留列名,还方便后续涉及DataFrame的操作

1. 单中括号 [] 访问
  • X['col'] → Series
  • 返回单列的一维数据结构
  • 相当于从 DataFrame 中"抽取"出一列
  • 项目的例子:X['KMeans_Cluster'] 是 Series
2. 双中括号 [[]] 访问
  • X[['col']] → DataFrame
  • 返回只含该列的二维数据结构
  • 相当于从 DataFrame 中"切片"出子表
  • 项目的例子:X[['KMeans_Cluster']] 是 1 列的 DataFrame

@浙大疏锦行


网站公告

今日签到

点亮在社区的每一天
去签到