Python 60天训练计划第18天实战挑战
聚类后的分析:推断簇的类型
知识点:
1. 推断簇含义的两个思路:先选特征和后选特征
2. 通过可视化图形借助AI定义簇的含义
3. 科研逻辑闭环:通过精度判断特征工程价值
现在需要给这个簇赋予实际的含义,一般当你赋予实际含义的时候,你需要根据某几个特征来赋予,但是源数据特征很多,如何选择特征呢?有2种思路:
1. 先选特征:你最开始聚类的时候,就选择了你想最后用来确定簇含义的特征,那么你需要选择一些特征来进行聚类,那么你最后确定簇含义的特征就是这几个特征,而非全部。如你想聚类消费者购买习惯,那么他过去的消费记录、购买记录、购买金额等等,这些特征都与消费者购买习惯有关,你可以使用这些特征来确定簇含义,一些其他的特征,如消费者年龄,工作行业则不考虑。—-适用于你本身就有构造某些明确含义的特征的情况。
2. 后选特征:最开始用全部特征来聚类,把其余特征作为 x,聚类得到的簇类别作为标签y构建监督模型,进而根据shap重要性筛选特征,来确定要根据哪些特征赋予含义。—使用于你想构造什么,目前还不清楚。
【上面这个思路好好学,这是真干货,构造具有价值的特征工程,妥妥的创新点和工作量】
本次数据采用day15泰坦尼克号预处理过后的数据
采用思路2:全部特征聚类(KMeans)–>其余特征为X,聚类特征(KMeans_Cluster)为标签Y–>用RF模型训练–>计算shap值–>绘制特征重要性条形图–>选择前几个重要特征,单独绘制这几个特征的分布图–>把这几张图给AI,并讲清楚背景,让它告诉你这几个簇的含义
# X里只有特征列(包含聚类列)
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) # 随机森林模型
# 不用划分训练集和测试集,直接拿全部数据来训练,应该这里是等会用shap来筛选特征的,不在意准确率
model.fit(x1, y1)
shap.initjs()
# 初始化 SHAP 解释器
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(x1) # 这个计算耗时
shap_values.shape # 第一维是样本数,第二维是特征数,第三维是类别数(簇数)
![]()
print(X[['KMeans_Cluster']].value_counts())

# --- 1. SHAP 特征重要性条形图 (Summary Plot - Bar) ---
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()

选前4个
# 此时判断一下这几个特征是离散型还是连续型
import pandas as pd
selected_features = ['Pclass','Embarked_Q','Fare','Parch']
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_discrete = ['Pclass','Embarked_Q','Parch']
selected_features_continuous = ['Fare']
绘制总的特征分布图
# X["Purpose_debt consolidation"].value_counts() # 统计每个唯一值的出现次数
import matplotlib.pyplot as plt
# 总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
#在 Python 里, plt.subplots() 函数会返回一个元组(tuple),
#这个元组包含两个元素。第一个元素是 Figure 对象,代表整个绘图窗口;
# 第二个元素是一个数组,里面包含多个 Axes 对象,每个 Axes 对象对应一个子图。
axes = axes.flatten()
#axes.flatten() 方法会把二维数组转换为一维数组,这样就可以通过单个索引来访问每个子图,像 axes[0] 、 axes[1] 等,在循环中使用更加方便。
#当调用 plt.subplots(2, 2) 时,返回的 axes 是一个二维数组,形状为 (2, 2)
#如果要逐个访问这些子图,使用二维索引会比较麻烦,例如 axes[0][0] 、 axes[0][1] 等。
for i, feature in enumerate(selected_features_discrete):
#enumerate(selected_features) :遍历 selected_features 列表,同时获取每个元素的索引 i 和元素值 feature
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')
axes[3].boxplot(X[selected_features_continuous])
axes[3].set_title('Boxplot of Fare')
axes[3].set_xlabel('Fare')
axes[3].set_ylabel('Value')
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_discrete):
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')
axes[3].boxplot(X_cluster0['Fare'])
axes[3].set_title('Boxplot of Fare')
axes[3].set_xlabel('Fare')
axes[3].set_ylabel('Value')
# 为整个大图添加标题
fig.suptitle('X_cluster0', fontsize=16)
# 调整子图布局,给大图标题留出空间
plt.subplots_adjust(top=0.9)
# 自动调整子图参数
plt.tight_layout()
plt.show()

# 再绘制簇1的分布图
import matplotlib.pyplot as plt
# 总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten()
for i, feature in enumerate(selected_features_discrete):
axes[i].hist(X_cluster1[feature], bins=20)
axes[i].set_title(f'Histogram of {feature}')
axes[i].set_xlabel(feature)
axes[i].set_ylabel('Frequency')
axes[3].boxplot(X_cluster1['Fare'])
axes[3].set_title('Boxplot of Fare')
axes[3].set_xlabel('Fare')
axes[3].set_ylabel('Value')
# 为整个大图添加标题
fig.suptitle('X_cluster1', fontsize=16)
# 调整子图布局,给大图标题留出空间
plt.subplots_adjust(top=0.9)
plt.tight_layout()
plt.show()

# 再绘制簇2的分布图
import matplotlib.pyplot as plt
# 总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten()
for i, feature in enumerate(selected_features_discrete):
axes[i].hist(X_cluster2[feature], bins=20)
axes[i].set_title(f'Histogram of {feature}')
axes[i].set_xlabel(feature)
axes[i].set_ylabel('Frequency')
axes[3].boxplot(X_cluster2['Fare'])
axes[3].set_title('Boxplot of Fare')
axes[3].set_xlabel('Fare')
axes[3].set_ylabel('Value')
# 为整个大图添加标题
fig.suptitle('X_cluster2', fontsize=16)
# 调整子图布局,给大图标题留出空间
plt.subplots_adjust(top=0.9)
plt.tight_layout()
plt.show()

# 先绘制簇3的分布图
import matplotlib.pyplot as plt
# 总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten()
for i, feature in enumerate(selected_features_discrete):
axes[i].hist(X_cluster3[feature], bins=20)
axes[i].set_title(f'Histogram of {feature}')
axes[i].set_xlabel(feature)
axes[i].set_ylabel('Frequency')
axes[3].boxplot(X_cluster3['Fare'])
axes[3].set_title('Boxplot of Fare')
axes[3].set_xlabel('Fare')
axes[3].set_ylabel('Value')
# 为整个大图添加标题
fig.suptitle('X_cluster3', fontsize=16)
# 调整子图布局,给大图标题留出空间
plt.subplots_adjust(top=0.9)
plt.tight_layout()
plt.show()

簇0(X_cluster0):
可能的群体特征:主要是三等舱乘客,非皇后镇登船,多为单独或带一个孩子/父母出行,票价较低。
簇1(X_cluster1):
可能的群体特征:多为头等舱乘客,非皇后镇登船,多为单独或带少量家人出行,支付较高票价。
簇2(X_cluster2):
可能的群体特征:主要是三等舱乘客,非皇后镇登船,多为单独或带一个孩子/父母出行,票价较低。
簇3(X_cluster3):
可能的群体特征:主要是三等舱乘客,非皇后镇登船,多带有一些家庭成员(如父母或孩子),票价范围较广。
总结:
1. 头部群组(簇1):可能对应高收入、高社会地位的乘客,多为头等舱,支付高票价。
2. 家庭群体(簇3):可能是有一定经济能力,但不一定非常高,且有较多随行家庭成员的乘客。
3. 大型群体(簇0和簇2):以三等舱乘客为主,可能代表经济条件相对较差、独自或仅带一个家人的乘客群体。
@浙大疏锦行
作者:only_only_you