DAY 18 推断聚类后簇的类型

@浙大疏锦行https://blog.csdn.net/weixin_45655710
知识点回顾:

  1. 推断簇含义的2个思路:先选特征和后选特征
  2. 通过可视化图形借助ai定义簇的含义
  3. 科研逻辑闭环:通过精度判断特征工程价值

**作业:**参考示例代码对心脏病数据集采取类似操作,并且评估特征工程后模型效果有无提升。

复制代码
# 导入必要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings

# 忽略警告信息,使输出更整洁
warnings.filterwarnings("ignore")

# 设置中文字体,确保图表中的中文能够正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # SimHei是Windows下常用的黑体
plt.rcParams['axes.unicode_minus'] = False  # 正常显示图表中的负号

# --- 步骤 1: 加载心脏病数据集 ---
print("--- 步骤 1: 加载心脏病数据集 ---")
try:
    # 读取名为 'heart.csv' 的数据文件
    data = pd.read_csv('heart.csv')
    print("✅ 心脏病数据加载成功!")
    print(f"数据形状 (行数, 列数): {data.shape}")
    print("\n数据前5行预览:")
    print(data.head())
    print("\n数据基本信息:")
    data.info()
    print("\n数据描述性统计:")
    print(data.describe())
except FileNotFoundError:
    print("❌ 错误: 'heart.csv' 文件未找到。请确保文件在此脚本的同一目录下。")
    exit() # 如果文件未找到,则退出脚本
except Exception as e:
    print(f"❌ 加载文件时发生错误: {e}")
    exit() # 如果发生其他错误,则退出脚本

# --- 步骤 2: 数据探索与预处理 ---
# 心脏病数据集的特征通常是数值型的,或者已经是编码好的类别型数值。
# 我们将检查是否有明显的字符串类型需要编码,并处理缺失值。

print("\n--- 步骤 2: 数据探索与预处理 ---")

# 2.1 检查是否有字符串类型的特征需要处理
# (根据常见的心脏病数据集,大部分特征应该是数值型)
object_columns = data.select_dtypes(include=['object']).columns
if not object_columns.empty:
    print(f"发现字符串类型的列: {object_columns.tolist()}")
    print("请根据具体列的含义决定如何编码(例如标签编码或独热编码)。")
    # 示例:如果需要,可以在此处添加对特定对象列的编码逻辑
    # 例如: data['某个对象列'] = data['某个对象列'].astype('category').cat.codes
else:
    print("✅ 数据集中没有发现需要特殊编码的字符串类型列。")

# 2.2 检查并处理缺失值
# 对于心脏病数据集,通常用中位数或均值填充数值型特征的缺失值
print("\n检查各列的缺失值数量:")
print(data.isnull().sum())

# 筛选出数值类型的列(包括整数和浮点数)
numerical_features = data.select_dtypes(include=[np.number]).columns.tolist()

if data.isnull().sum().sum() > 0: # 如果存在任何缺失值
    print("\n正在使用中位数填充数值型特征的缺失值...")
    for feature in numerical_features:
        if data[feature].isnull().any(): # 如果该列存在缺失值
            median_value = data[feature].median() # 计算中位数
            data[feature].fillna(median_value, inplace=True) # 用中位数填充
            print(f"   列 '{feature}' 的缺失值已用中位数 {median_value} 填充。")
    print("✅ 缺失值处理完成。")
else:
    print("✅ 数据集中没有缺失值。")



print("\n--- 步骤 3: 划分特征与标签 ---")
# 3.1 分离特征 (X) 和标签 (y)
# 假设目标变量(是否患有心脏病)的列名为 'target'
X = data.drop(['thalach'], axis=1)  # 特征集,移除目标变量列
y = data['thalach']                 # 标签集,只包含目标变量列

print("\n--- 步骤 4: 划分训练集和测试集 ---")
# 3.2 按照8:2的比例划分训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.2,     # 20% 的数据作为测试集
    random_state=42,   # 设置随机种子以确保结果可复现
)
print("✅ 训练集和测试集已成功划分。")
print(f"训练集特征 X_train 的形状: {X_train.shape}")
print(f"测试集特征 X_test 的形状: {X_test.shape}")
print(f"训练集标签 y_train 的形状: {y_train.shape}")
print(f"测试集标签 y_test 的形状: {y_test.shape}")

print("\n--- 数据预处理流程结束 ---")

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}")

# 绘制评估指标图
plt.figure(figsize=(15, 10))

# 肘部法则图(Inertia)
plt.subplot(2, 2, 1)
plt.plot(k_range, inertia_values, marker='o')
plt.title('肘部法则确定最优聚类数 k(惯性,越小越好)')
plt.xlabel('聚类数 (k)')
plt.ylabel('惯性')
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()

# 提示用户选择 k 值
selected_k = 4

# 使用选择的 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

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)  # 随机森林模型
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 = ['sex', 'target',
                     'age', 'slope']

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

# 先绘制簇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):
    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()

三个簇的总结与定义 (基于心脏病数据集特征)

  • 第一个簇 (簇0) - 定义为:高龄高风险指示型

    • 特征总结 :该簇的个体表现出相对较高的年龄 (age)。其静息血压 (trestbps) 和胆固醇 (chol) 水平均处于较高水平。运动引起的心绞痛 (exng) 比例可能较高,并且运动相关的ST段压低 (oldpeak) 数值也可能偏高。主要血管阻塞数量 (ca) 可能较多。最大心率 (thalach) 可能相对较低。
    • 定义依据:该簇的多个关键生理指标(高龄、高血压、高胆固醇、运动耐受性差迹象、血管阻塞情况)均指向较高的心血管疾病风险。这些特征共同构成了一组典型的高风险因素。
  • 第二个簇 (簇1) - 定义为:中青年相对健康型

    • 特征总结 :此簇的个体年龄 (age) 相对较低或处于中年水平。其静息血压 (trestbps) 和胆固醇 (chol) 水平可能处于中等或正常范围。最大心率 (thalach) 可能相对较高,而运动相关的ST段压低 (oldpeak) 数值可能较低。运动诱发心绞痛 (exng) 的比例较低,主要血管阻塞数量 (ca) 也可能较少。
    • 定义依据:该簇的各项生理指标(年龄、血压、胆固醇、运动能力)整体上表现出比其他簇更健康的状态,或风险因素较少,因此定义为相对健康或风险较低的类型。
  • 第三个簇 (簇2) - 定义为:特定风险因素突显型 (例如,运动反应异常或特定胸痛类型)

    • 特征总结 :这个簇的特点可能不像前两个那样整体性地"好"或"差",而是在某些特定指标上表现突出。例如,他们的年龄可能跨度较大,但可能普遍具有某种特定类型的胸痛 (cp),或者其最大心率 (thalach) 异常高或低,或者运动ST段的斜率 (slope) 呈现特定模式。主要血管阻塞数 (ca) 可能处于中等水平,但可能伴随其他特定风险因子。
    • 定义依据:该簇可能不是由普遍的年龄或基础健康状况来定义,而是由一两个关键的、指示特定心脏问题的生理指标或症状组合所区分开来。其风险程度可能介于前两者之间,或者表现为一种特定类型的心血管问题倾向。
相关推荐
先做个垃圾出来………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 小时前
【机器学习】支持向量机
算法·机器学习·支持向量机