Python训练营第18天打卡:如何推断数据簇类型

一.数据预处理

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sans
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 导入心脏病项目的数据
data = pd.read_csv(r'C:\Users\ggggg\Desktop\python60-days-challenge-master (1)\python60-days-challenge-master\heart.csv')
# 查看数据
data.info()
data.head()
data.isnull().sum()
进行数据类型转换,先分离出数据中的连续特征和离散特征
# 连续特征:数值型且唯一值比例高
continuous_feature = [col for col in data.columns
                     if data[col].dtype in ['float64', 'int64']
                     and data[col].nunique()/len(data) > 0.1] # 唯一值超过10%
# 离散特征:非数值型或低基数数值
discrete_features = [col for col in data.columns
                   if col not in continuous_features]
print(discrete_features)
print(continuous_features) 

对离散特征进行编码,先查看内容
for feature in discrete_features:
    print(f"\n{feature}的唯一值:")
    print(data[feature].value_counts())
对'sex',  'fbs',  'target'进行独热编码
nominal_features = ['sex','fbs',  'target'] # 不存在顺序关系的特征
# 独热编码
data = pd.get_dummies(data, columns=nominal_features, drop_first=True)
data2 = pd.read_csv("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)
对'cp'进行映射编码
cp_mapping = {0: 0, 1: 1, 2: 2, 3: 3}
if 'cp' in data.columns:
    data['cp'] = data['cp'].map(cp_mapping)
对连续特征进行缺失值填补
continuous_features = data.select_dtypes(include=['int64', 'float64']).columns.tolist()  
 
for feature in continuous_features:
    mode_value = data[feature].mode()[0]
    data.loc[:, feature] = data[feature].fillna(mode_value)
数据划分
# 划分训练集和测试集
from sklearn.model_selection import train_test_split
X = data.drop(['target_1'], axis=1) # 特征,axis=1表示按列删除
y = data['target_1']
# 划分数据集,20%作为测试集,随机种子为42
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练集和测试集的形状
print(f"训练集形状:{X_train.shape}, 测试集形状:{X_test.shape}")

二.导入聚类所需的相关库与评估指标

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

删除聚类标签列,并构建随机森林模型进行训练
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可视化
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 = ['thalach', 'oldpeak',
                     'slope', 'cp']

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} 可能是连续型变量')

生成特征分布图
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]
1.先绘制簇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()

2.绘制簇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()

3.绘制簇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()

定义簇

簇 1:低特征值簇
  • 定义依据:对于各变量(thalach、oldpeak、slope、cp ),该簇包含在各变量上取值普遍较低的数据点。比如在 thalach 上,最大心率处于较低水平区间;oldpeak 中,ST 段压低程度较小;slope 里,ST 段斜率值较小;cp 中,对应出现频率相对较低且数值较小的胸痛类型(若数值大小有实际意义关联) 
  • 数据特点:整体表现为各项生理指标或特征处于相对不活跃、轻微或低水平状态
  • 簇 2:中等特征值簇
  • 定义依据:数据点在各变量上的取值处于中间水平。thalach 的最大心率既非极低也非极高;oldpeak 的 ST 段压低程度适中;slope 的 ST 段斜率处于中间范围;cp 中是出现频率中等的胸痛类型
  • 数据特点:各项指标呈现出相对平衡、常规的状态,不偏向于极端情况
  • 簇 3:高特征值簇
  • 定义依据:由在各变量上取值普遍较高的数据点组成。thalach 中最大心率处于较高区间;oldpeak 里 ST 段压低程度较大;slope 的 ST 段斜率值较大;cp 中是出现频率相对较高且数值较大的胸痛类型(若数值大小有实际意义关联) 
  • 数据特点:各项生理指标或特征处于相对活跃、显著或高水平状态
  • @浙大疏锦行

    作者:weixin_57577876

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python训练营第18天打卡:如何推断数据簇类型

    发表回复