python打卡day18
聚类后的分析:推断簇的类型
知识点回顾:
- 推断簇含义的2个思路:先选特征和后选特征
- 通过可视化图形借助ai定义簇的含义
- 科研逻辑闭环:通过精度判断特征工程价值
作业:参考示例代码对心脏病数据集采取类似操作,并且评估特征工程后模型效果有无提升。
昨天的内容只是使用三种聚类算法完成了聚类以及结果可视化,以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
@浙大疏锦行