Day 18 推断聚类后簇的类型

@浙大疏锦行

今日任务:

  1. 推断簇含义的2个思路:先选特征和后选特征
  2. 通过可视化图形借助ai定义簇的含义
  3. 通过精度判断特征工程价值
  4. 参考示例代码对心脏病数据集采取类似操作,并且评估特征工程后模型效果有无提升。

在Day 17 的学习中,了解了三种聚类算法的使用。但是如果仅得到聚类后的结果而不赋予实际含义,那么聚类将毫无意义。聚类回答了"是什么"的问题,而给聚类标签赋予实际意义则解决"为什么"的问题。

在赋予实际意义之前,需要有特定的特征,但是这个特征如何确定?

目前常用的思路有两种,分别是目标明确的设计型聚类探索驱动的解释型聚类:

  • 设计型:在聚类之前,根据背景知识选定研究目标,进而确定选取的特征,用来确定簇含义。
  • 解释型:聚类前不知道目的,尝试用探索 的方式找到一些现象。因而选择使用全部特征 进行聚类后,再将某特征聚类后得到的簇类别作为标签y,其余特征作为x,通过重要性排序得到关键特征,再赋予其含义。

在实际使用的过程中,可以将两种思路相结合:先采取思路二进行全面的筛选与探索,然后利用思路一进行深入的设计。

下面通过信贷数据集,进行思路2的实操。

KMeans聚类

将经过预处理的数据(注意标准化),对所有特征进行KMeans算法聚类(确定最佳k值)

python 复制代码
#标准化数据
from sklearn.preprocessing import StandardScaler
standard_scacler = StandardScaler()
X_scaled = standard_scacler.fit_transform(X)
#划分数据集
from sklearn.model_selection import train_test_split
X = data.drop(['Credit Default'], axis=1)  # 特征,axis=1表示按列删除
y = data['Credit Default'] # 标签
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)  # 80%训练集,20%测试集
python 复制代码
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score,calinski_harabasz_score,davies_bouldin_score
from sklearn.decomposition import PCA

#最佳k值的确定
k_range = range(2,11) #定义k的取值范围
inertia_scores = []
silhouette_scores = []
ch_scores = []
db_scores = []

for k in k_range:
    kmeans = KMeans(n_clusters=k,random_state=42) #实例化
    k_labels = kmeans.fit_predict(X_scaled) #训练
    #计算评估指标
    inertia = kmeans.inertia_ #惯性值
    silhouette = silhouette_score(X_scaled,k_labels)
    ch_score = calinski_harabasz_score(X_scaled,k_labels)
    db_score = davies_bouldin_score(X_scaled,k_labels)
    #添加
    inertia_scores.append(inertia) 
    silhouette_scores.append(silhouette)
    ch_scores.append(ch_score)
    db_scores.append(db_score)
    #打印参数
    print(f'k={k},惯性:{inertia:.2f}, 轮廓系数:{silhouette:.2f}, CH指数:{ch_score:.2f}, DB指数:{db_score:.2f}')


#可视化
plt.figure(figsize=(12,10))

#k与惯性
plt.subplot(2,2,1)
plt.plot(k_range,inertia_scores,marker='o')
plt.title('肘部法则')
plt.xlabel('k')
plt.ylabel('inertia_score')
plt.grid(True)

# 轮廓系数图
plt.subplot(2, 2, 2)
plt.plot(k_range, silhouette_scores, marker='o', color='orange')
plt.title('轮廓系数确定最优聚类数 k(越大越好)')
plt.xlabel('聚类数 (k)')
plt.ylabel('轮廓系数')
plt.grid(True)

# CH 指数图
plt.subplot(2, 2, 3)
plt.plot(k_range, ch_scores, marker='o', color='green')
plt.title('Calinski-Harabasz 指数确定最优聚类数 k(越大越好)')
plt.xlabel('聚类数 (k)')
plt.ylabel('CH 指数')
plt.grid(True)

# DB 指数图
plt.subplot(2, 2, 4)
plt.plot(k_range, db_scores, marker='o', color='red')
plt.title('Davies-Bouldin 指数确定最优聚类数 k(越小越好)')
plt.xlabel('聚类数 (k)')
plt.ylabel('DB 指数')
plt.grid(True)

plt.tight_layout()
plt.show()
python 复制代码
#根据最佳的k值进行训练,并使用PCA降维可视化
selected_k = 3 #此处便于后续分析,选择数值3,实际不是最佳值
#建模训练
kmeans = KMeans(n_clusters=selected_k,random_state=42)
k_labels = kmeans.fit_predict(X_scaled)
X['Kmeans_clusters'] = k_labels #将聚类结果添加入原始数据
#PCA降维到2D
pca = PCA(n_components=2) 
x_pca = pca.fit_transform(X_scaled)
#可视化
sns.scatterplot(x=x_pca[:,0],y=x_pca[:,1],hue=k_labels,palette='viridis')
plt.xlabel('pca_component_1')
plt.ylabel('pca_component_2')
plt.show()

建模训练与特征筛选

将聚类后的簇类别作为标签y,剩余特征作为x。然后选择模型进行训练,借助shap库进行特征重要性的排序。

python 复制代码
#特征筛选
#特征与标签划分
x1 = X.drop(columns=['Kmeans_clusters'],axis=1)
y1 = X['Kmeans_clusters']
from sklearn.ensemble import RandomForestClassifier
rf_model = RandomForestClassifier(random_state=42) #实例化
rf_model.fit(x1,y1) #训练
python 复制代码
import shap
explainer = shap.TreeExplainer(model=rf_model) #初始化解释器
shap_values = explainer.shap_values(X=x1) #计算shap值

#绘图
shap.initjs()#初始化JavaScript可视化功能,确保后续可视化图表正常工作
shap.summary_plot(shap_values[:,:,0],x1,plot_type='bar',show=False)
plt.title('特征重要性排序条形图')
plt.show()

通过特征重要性条形图可以发现,前几个特征的shap值较高。为方便后续画图,选取前4个特征。这里判断了这几个特征的数据类型,可能是为了后续可视化特征分布的图形选择。

python 复制代码
#特征的数据类型判断
selected_features = ['Purpose_debt consolidation','Bankruptcies','Number of Credit Problems','Purpose_other']
#判断特征为离散型还是连续型
for i in selected_features:
    unique_counts = X[i].nunique() # 唯一值指的是在某一列或某个特征中,不重复出现的值
    print(f'{i}有{unique_counts}个变量') # 连续型变量通常有很多唯一值,而离散型变量的唯一值较少
    if unique_counts < 10: # 这里 10 是一个经验阈值,可以根据实际情况调整
        print(f'{i}为离散特征')
    else:
        print(f'{i}为连续特征')

可视化与含义赋予

找到关键特征后,需要将每个簇别对应的特征分布展示出来,从而分析其中的特点,得到对应的"用户画像"。

具体而言,先筛选出不同簇的DataFrame,然后分别绘制对应的特征分布图即可。

python 复制代码
#提取不同的簇别
x_cluster0 = X[X['Kmeans_clusters']==0]
x_cluster1 = X[X['Kmeans_clusters']==1]
x_cluster2 = X[X['Kmeans_clusters']==2]

#可视化簇别
#簇别0
fig,axes = plt.subplots(2,2,figsize=(12,10))
axes = axes.flatten()

for index,value in enumerate(selected_features):
    sns.histplot(x_cluster0[value],ax=axes[index],bins=20)#使用countplot,由于float型,导致跑得很慢
    axes[index].set_xlabel(f'{value}')
    axes[index].set_title(f'The Countplot of {value}')

plt.tight_layout()
plt.show()
python 复制代码
#可视化簇别
#簇别1
fig,axes = plt.subplots(2,2,figsize=(12,10))
axes = axes.flatten()

for index,value in enumerate(selected_features):
    sns.histplot(x_cluster1[value],ax=axes[index],bins=20)#使用countplot,由于float型,导致跑得很慢
    axes[index].set_xlabel(f'{value}')
    axes[index].set_title(f'The Countplot of {value}')

plt.tight_layout()
plt.show()
python 复制代码
#可视化簇别
#簇别2
fig,axes = plt.subplots(2,2,figsize=(12,10))
axes = axes.flatten()

for index,value in enumerate(selected_features):
    sns.histplot(x_cluster2[value],ax=axes[index],bins=20)#使用countplot,由于float型,导致跑得很慢
    axes[index].set_xlabel(f'{value}')
    axes[index].set_title(f'The Countplot of {value}')

plt.tight_layout()
plt.show()

完成可视化操作后,就需要对图进行分析,无论是借助自己的相关知识还是AI辅助分析,最终基本上可以得到不同簇别的特点,从而构建新的标签 。后续只需按照之前的步骤进行处理(独热编码),建模训练,对比特征提取前后的效果,看模型的精度是否有所提高。

下面分别是簇别0,簇别1及簇别2的特征分布图

下面是借助AI分析得到的簇别含义的定义:

主要特征 风险等级 客户画像 建议策略
0 债务整合 + 1次破产 + 多信用问题 高风险 财务困难、曾破产、需重组 审慎放贷,加强贷后监控,考虑更高利率或担保
1 其他用途 + 无破产 + 无信用问题 低风险 收入稳定、信用良好、消费驱动 可放宽审批,推荐长期产品
2 债务整合 + 无破产 + 无信用问题 低风险 信用优秀、主动优化债务 可优先授信,提供优惠利率或自动化服务

作业:心脏病数据集练习

针对心脏病数据集,练习上面的操作流程。

数据预处理与KMeans聚类,day17已练习过。只说明今日的内容。

python 复制代码
#特征筛选
#特征与标签划分
x1 = X.drop(columns=['KMeans_clusters'],axis=1)
y1 = X['KMeans_clusters']
from sklearn.ensemble import RandomForestClassifier
rf_model = RandomForestClassifier(random_state=42) #实例化
rf_model.fit(x1,y1) #训练
import shap
explainer = shap.TreeExplainer(model=rf_model) #初始化解释器
shap_values = explainer.shap_values(X=x1) #计算shap值
#绘图
shap.initjs()#初始化JavaScript可视化功能,确保后续可视化图表正常工作
shap.summary_plot(shap_values[:,:,0],x1,plot_type='bar',show=False)
plt.title('特征重要性排序条形图')
plt.show()
python 复制代码
#特征的数据类型判断
selected_features = ['thalach','trestbps','oldpeak','chol']
#判断特征为离散型还是连续型
for i in selected_features:
    unique_counts = X[i].nunique() # 唯一值指的是在某一列或某个特征中,不重复出现的值
    print(f'{i}有{unique_counts}个变量') # 连续型变量通常有很多唯一值,而离散型变量的唯一值较少
    if unique_counts < 10: # 这里 10 是一个经验阈值,可以根据实际情况调整
        print(f'{i}为离散特征')
    else:
        print(f'{i}为连续特征')
python 复制代码
#提取不同的簇别
x_cluster0 = X[X['KMeans_clusters']==0]
x_cluster1 = X[X['KMeans_clusters']==1]
x_cluster2 = X[X['KMeans_clusters']==2]
x_cluster3 = X[X['KMeans_clusters']==3]

#可视化簇别
#簇别0
fig,axes = plt.subplots(2,2,figsize=(12,10))
axes = axes.flatten()

for index,value in enumerate(selected_features):
    sns.histplot(x_cluster0[value],ax=axes[index],bins=20)#连续特征
    axes[index].set_xlabel(f'{value}')
    axes[index].set_title(f'The Countplot of {value}')

plt.tight_layout()
plt.show()

剩下的几个簇别,绘图类似,最后得到下面四个图:

通过AI分析后得到下面的结果:

thalach trestbps oldpeak chol 临床画像 风险等级
0 偏低 正常 轻微缺血 正常 轻度心功能不全 中等
1 正常偏高 偏高 无缺血 偏高 高血压+高血脂 中高
2 显著偏高 正常偏低 无缺血 正常 心血管健康
3 偏低 偏高 显著缺血 偏高 多重风险+心肌缺血
相关推荐
查士丁尼·绵3 小时前
笔试-基站维护
python
deephub3 小时前
REFRAG技术详解:如何通过压缩让RAG处理速度提升30倍
人工智能·python·大语言模型·rag
唐叔在学习3 小时前
venv - Python最佳的轻量化环境隔离方式
后端·python
小白学大数据4 小时前
Python爬虫数据可视化:深度分析贝壳成交价格趋势与分布
爬虫·python·信息可视化
小小测试开发4 小时前
Python Arrow库:告别datetime繁琐,优雅处理时间与时区
开发语言·前端·python
我的xiaodoujiao4 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 18--测试框架Pytest基础 2--插件和参数化
python·学习·测试工具·pytest
程序员的奶茶馆4 小时前
Python 数据结构面试真题:如何实现 LRU 缓存机制
python·面试
星期天要睡觉4 小时前
深度学习——基于 ResNet18 的图像分类训练
pytorch·python·机器学习
林炳然4 小时前
Python-Basic Day-1 基本元素(数字、字符串)
python