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

  • – Pclass:主要集中在三等舱,有少量二等舱乘客。
  • – Embarked_Q:绝大多数乘客不是在皇后镇登船的。
  • – Parch:大部分乘客没有父母或孩子同行(0个),少量有1个。
  • – Fare:票价分布较集中,多在10到30之间,有少数较高票价。
  • 可能的群体特征:主要是三等舱乘客,非皇后镇登船,多为单独或带一个孩子/父母出行,票价较低。

    簇1(X_cluster1):

  • – Pclass:主要集中在头等舱,少量二等舱乘客。
  • – Embarked_Q:绝大多数乘客不是在皇后镇登船的。
  • – Parch:大部分乘客没有父母或孩子同行(0个),少量有1个或2个。
  • – Fare:票价分布范围较广,中位数较高,有较多高票价的异常值。
  • 可能的群体特征:多为头等舱乘客,非皇后镇登船,多为单独或带少量家人出行,支付较高票价。

    簇2(X_cluster2):

  • – Pclass:主要集中在三等舱,极少数一等舱和二等舱乘客。
  • – Embarked_Q:绝大多数乘客不是在皇后镇登船的。
  • – Parch:大部分乘客没有父母或孩子同行(0个),少量有1个。
  • – Fare:票价分布较集中,中位数较低,有少数较高票价。
  • 可能的群体特征:主要是三等舱乘客,非皇后镇登船,多为单独或带一个孩子/父母出行,票价较低。

    簇3(X_cluster3):

  • – Pclass:主要集中在三等舱,少量二等舱乘客。
  • – Embarked_Q:绝大多数乘客不是在皇后镇登船。
  • – Parch:乘客中有不同数量的父母或孩子同行(1到6个),以2个为主。
  • – Fare:票价分布范围较广,中位数适中,有较高票价的异常值。
  • 可能的群体特征:主要是三等舱乘客,非皇后镇登船,多带有一些家庭成员(如父母或孩子),票价范围较广。

    总结:

    1. 头部群组(簇1):可能对应高收入、高社会地位的乘客,多为头等舱,支付高票价。

    2. 家庭群体(簇3):可能是有一定经济能力,但不一定非常高,且有较多随行家庭成员的乘客。

    3. 大型群体(簇0和簇2):以三等舱乘客为主,可能代表经济条件相对较差、独自或仅带一个家人的乘客群体。

    @浙大疏锦行

    作者:only_only_you

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python 60天训练计划第18天实战挑战

    发表回复