DAY 18 推断聚类后簇的类型

在聚类分析中,推断簇的类型是理解数据内在结构和业务意义的关键步骤。以下是系统化的推断方法及常见簇类型的总结:

一、簇的基本类型

  1. ​明显分离的簇​

    • ​特征​:不同簇中任意两点之间的距离 > 簇内任意两点间距离。
    • ​适用场景​:数据自然分组清晰,如生物学中的物种分类。
    • ​示例​:球形或任意形状的分离簇(图10-2a)。
  2. ​基于原型的簇(中心型簇)​

    • ​特征​:簇内对象到质心(均值)的距离 < 到其他簇质心的距离。
    • ​典型算法​:K-Means、层次聚类。
    • ​局限性​:倾向于生成球形簇,对不规则形状效果差。
  3. ​基于密度的簇​

    • ​特征​:通过高密度区域识别,可处理噪声和离群点。
    • ​典型算法​:DBSCAN。
    • ​优势​:适用于缠绕或不规则形状(如哑铃状簇)。
  4. ​基于连片的簇​

    • ​特征​:相邻对象距离在阈值内即归为同簇,依赖连通性。
    • ​适用场景​:图结构数据(如社交网络)。

二、推断簇含义的方法

  1. ​先选特征法​

    • ​步骤​:聚类前选择与业务强相关的特征(如消费记录)。
    • ​优点​:结果可直接解释,避免无关特征干扰。
    • ​案例​:分析用户购买习惯时,仅用"消费频率""金额"等特征聚类。
  2. ​后选特征法​

    • ​步骤​
      • 用全部特征聚类,生成簇标签;
      • 将簇标签作为目标变量,构建分类模型(如随机森林);
      • 通过SHAP值、特征重要性筛选关键特征解释簇
  3. ​可视化辅助分析​

    • 降维(如PCA)后绘制散点图,观察簇分布;
    • 结合业务知识标注簇类型(如"高风险心脏病患者簇")。

三、实际应用注意事项

  1. ​评估簇的合理性​

    • 检查每个簇的样本量(避免过小无意义);
    • 结合轮廓系数、CH指数等指标优化聚类参数。
  2. ​业务逻辑闭环​

    • 将聚类结果作为新特征加入监督模型(如分类预测);
    • 通过精度提升验证特征工程价值(如心脏病数据集预测)。
  3. ​避免常见误区​

    • 非球状簇勿强制用K-Means(可选DBSCAN);
    • 高维数据需先降维再解释,避免"维度灾难"。

总结

推断簇类型需结合​​数据特性​ ​(形状、密度)与​​业务目标​​。优先尝试"后选特征法"+可视化全面探索,再通过"先选特征法"聚焦业务解释。最终需以监督模型验证聚类的实际价值(如预测效果提升)。若效果未达预期,可尝试过采样(SMOTE)或调整聚类算法进一步优化。

代码实现:

python 复制代码
# 先运行之前预处理好的代码
import pandas as pd
import pandas as pd    #用于数据处理和分析,可处理表格数据。
import numpy as np     #用于数值计算,提供了高效的数组操作。
import matplotlib.pyplot as plt    #用于绘制各种类型的图表
import seaborn as sns   #基于matplotlib的高级绘图库,能绘制更美观的统计图形。
import warnings
warnings.filterwarnings("ignore")
 
 # 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统常用黑体字体
plt.rcParams['axes.unicode_minus'] = False    # 正常显示负号
data = pd.read_csv('python60-days-challenge-master/data.csv')    #读取数据
 
 
# 先筛选字符串变量 
discrete_features = data.select_dtypes(include=['object']).columns.tolist()
# Home Ownership 标签编码
home_ownership_mapping = {
    'Own Home': 1,
    'Rent': 2,
    'Have Mortgage': 3,
    'Home Mortgage': 4
}
data['Home Ownership'] = data['Home Ownership'].map(home_ownership_mapping)
 
# Years in current job 标签编码
years_in_job_mapping = {
    '< 1 year': 1,
    '1 year': 2,
    '2 years': 3,
    '3 years': 4,
    '4 years': 5,
    '5 years': 6,
    '6 years': 7,
    '7 years': 8,
    '8 years': 9,
    '9 years': 10,
    '10+ years': 11
}
data['Years in current job'] = data['Years in current job'].map(years_in_job_mapping)
 
# Purpose 独热编码,记得需要将bool类型转换为数值
data = pd.get_dummies(data, columns=['Purpose'])
data2 = pd.read_csv('python60-days-challenge-master/data.csv') # 重新读取数据,用来做列名对比
list_final = [] # 新建一个空列表,用于存放独热编码后新增的特征名
for i in data.columns:
    if i not in data2.columns:
       list_final.append(i) # 这里打印出来的就是独热编码后的特征名
for i in list_final:
    data[i] = data[i].astype(int) # 这里的i就是独热编码后的特征名
 
 
 
# Term 0 - 1 映射
term_mapping = {
    'Short Term': 0,
    'Long Term': 1
}
data['Term'] = data['Term'].map(term_mapping)
data.rename(columns={'Term': 'Long Term'}, inplace=True) # 重命名列
continuous_features = data.select_dtypes(include=['int64', 'float64']).columns.tolist()  #把筛选出来的列名转换成列表
 
 # 连续特征用中位数补全
for feature in continuous_features:     
    mode_value = data[feature].mode()[0]            #获取该列的众数。
    data[feature].fillna(mode_value, inplace=True)          #用众数填充该列的缺失值,inplace=True表示直接在原数据上修改。
 
# 最开始也说了 很多调参函数自带交叉验证,甚至是必选的参数,你如果想要不交叉反而实现起来会麻烦很多
# 所以这里我们还是只划分一次数据集
from sklearn.model_selection import train_test_split
X = data.drop(['Credit Default'], axis=1)  # 特征,axis=1表示按列删除
y = data['Credit Default'] # 标签
# # 按照8:2划分训练集和测试集
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)  # 80%训练集,20%测试集
python 复制代码
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns
 
# 标准化数据(聚类前通常需要标准化)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# X_scaled
 
 
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
import matplotlib.pyplot as plt
import seaborn as sns
 
# 评估不同 k 值下的指标
k_range = range(2, 11)  # 测试 k 从 2 到 10
inertia_values = []
silhouette_scores = []
ch_scores = []
db_scores = []
 
for k in k_range:
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans_labels = kmeans.fit_predict(X_scaled)
    inertia_values.append(kmeans.inertia_)  # 惯性(肘部法则)
    silhouette = silhouette_score(X_scaled, kmeans_labels)  # 轮廓系数
    silhouette_scores.append(silhouette)
    ch = calinski_harabasz_score(X_scaled, kmeans_labels)  # CH 指数
    ch_scores.append(ch)
    db = davies_bouldin_score(X_scaled, kmeans_labels)  # DB 指数
    db_scores.append(db)
    print(f"k={k}, 惯性: {kmeans.inertia_:.2f}, 轮廓系数: {silhouette:.3f}, CH 指数: {ch:.2f}, DB 指数: {db:.3f}")
 
# 提示用户选择 k 值
selected_k = 3 # 这里选择3后面好分析,也可以根据图选择最佳的k值
 
# 使用选择的 k 值进行 KMeans 聚类
kmeans = KMeans(n_clusters=selected_k, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)
X['KMeans_Cluster'] = kmeans_labels
 
# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
 
# KMeans 聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=kmeans_labels, palette='viridis')
plt.title(f'KMeans Clustering with k={selected_k} (PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()
 
# 打印 KMeans 聚类标签的前几行
print(f"KMeans Cluster labels (k={selected_k}) added to X:")
print(X[['KMeans_Cluster']].value_counts())
 
X.columns # 多出的列名是在做预处理的时候Purpose列被独热编码,生成了多个二进制列。它们代表了原始分类变量的不同类别。
 
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)  # 随机森林模型
# n_estimators 这个参数指定了随机森林中决策树的数量。
model.fit(x1, y1)  # 训练模型,此时无需在意准确率 直接全部数据用来训练了
 
 
shap.initjs()
# 初始化 SHAP 解释器
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(x1) # 这个计算耗时
shap_values.shape # 第一维是样本数,第二维是特征数,第三维是类别数
 
 
# --- 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()
 
 
# 此时判断一下这几个特征是离散型还是连续型
import pandas as pd
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} 可能是连续型变量')
 
 
# X["Purpose_debt consolidation"].value_counts() # 统计每个唯一值的出现次数
import matplotlib.pyplot as plt
 
# 总样本中的前四个重要性的特征分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten()
 
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()
 
 
# 绘制出每个簇对应的这四个特征的分布图
X[['KMeans_Cluster']].value_counts()
 
 
# 分别筛选出每个簇的数据
X_cluster0 = X[X['KMeans_Cluster'] == 0]
X_cluster1 = X[X['KMeans_Cluster'] == 1]
X_cluster2 = X[X['KMeans_Cluster'] == 2]
 
 
# 先绘制簇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):
    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')
 
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):
    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')
 
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):
    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')
 
plt.tight_layout()
plt.show()
相关推荐
先做个垃圾出来………5 小时前
《机器学习系统设计》
人工智能·机器学习
my_q5 小时前
机器学习与深度学习08-随机森林02
深度学习·随机森林·机器学习
xiaoli23277 小时前
机器学习——SVM
人工智能·机器学习·支持向量机
南瓜胖胖8 小时前
【R语言编程绘图-mlbench】
开发语言·机器学习·r语言
宋一诺338 小时前
机器学习——放回抽样
人工智能·机器学习
Ao0000008 小时前
机器学习——主成分分析PCA
人工智能·机器学习
硅谷秋水9 小时前
Impromptu VLA:用于驾驶视觉-语言-动作模型的开放权重和开放数据
人工智能·机器学习·计算机视觉·语言模型·自动驾驶
宋一诺3310 小时前
机器学习——随机森林算法
算法·随机森林·机器学习
越轨11 小时前
【Pytorch学习笔记】模型模块08——AlexNet模型详解
人工智能·pytorch·笔记·深度学习·学习·机器学习
2301_8203010212 小时前
【机器学习】支持向量机
算法·机器学习·支持向量机