Python训练营打卡DAY18
聚类后的分析:推断簇的类型
知识点回顾:
- 推断簇含义的2个思路:先选特征和后选特征
- 通过可视化图形借助ai定义簇的含义
- 科研逻辑闭环:通过精度判断特征工程价值
作业:参考示例代码对心脏病数据集采取类似操作,并且评估特征工程后模型效果有无提升。
最开始用全部特征来聚类,把其余特征作为 x,聚类得到的簇类别作为标签构建监督模型,进而根据重要性筛选特征,来确定要根据哪些特征赋予含义。
最终大致分为5类:高血糖/非典型胸痛;低风险/非典型胸痛;高风险/典型心绞痛;中风险/典型心绞痛;年轻/低风险/多样症状。
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']
plt.rcParams['axes.unicode_minus'] = False
data = pd.read_csv('heart.csv')
data.head()
data.isnull().sum()from sklearn.model_selection import train_test_split
X = data.drop(['target'], axis=1)
y = data['target']
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)
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#kmeans
k_range = range(2, 11)
inertia_values = [] # 惯性(肘部法则)
silhouette_scores = [] # 轮廓系数
ch_scores = [] # CH 指数
db_scores = [] # DB 指数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=5
selected_k = 5
kmeans = KMeans(n_clusters=selected_k, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)
X['KMeans_Cluster'] = kmeans_labels
X['KMeans_Cluster'].value_counts()
X.head()
# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# KMeans 聚类结果可视化
plt.figure(figsize=(10, 6))
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 = np.array(shap_values)
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 = ['fbs', 'cp','age', 'ca','thalach','trestbps']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(3, 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_cluster0 = X[X['KMeans_Cluster'] == 0]
X_cluster1 = X[X['KMeans_Cluster'] == 1]
X_cluster2 = X[X['KMeans_Cluster'] == 2]
X_cluster3 = X[X['KMeans_Cluster'] == 3]
X_cluster4 = X[X['KMeans_Cluster'] == 4]
# 先绘制簇0的分布图
import matplotlib.pyplot as plt
# 重要性的特征分布图
fig, axes = plt.subplots(3, 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(3, 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(3, 2, figsize=(12, 8))
axes = axes.flatten()for i, feature in enumerate(selected_features): # 这里的enumerate函数会返回一个迭代器,每次迭代返回一个索引和对应的值。axes[i].hist(X_cluster2[feature], bins=20) # 这里的bins参数表示直方图的柱子数量,这里设置为20,表示将数据分成20个区间。axes[i].set_title(f'Histogram of {feature}') # 这里的set_title函数会设置直方图的标题。axes[i].set_xlabel(feature) # 这里的set_xlabel函数会设置直方图的x轴标签。axes[i].set_ylabel('Frequency') # 这里的set_ylabel函数会设置直方图的y轴标签。plt.tight_layout() # 这里的tight_layout函数会自动调整子图之间的间距,使得子图之间不会重叠。
plt.show() # 这里的show函数会显示所有的子图。
# 再绘制簇3的分布图
import matplotlib.pyplot as plt
# 重要性的特征分布图
fig, axes = plt.subplots(3, 2, figsize=(12, 8))
axes = axes.flatten()for i, feature in enumerate(selected_features):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')plt.tight_layout()
plt.show()
# 再绘制簇4的分布图
import matplotlib.pyplot as plt
# 重要性的特征分布图
fig, axes = plt.subplots(3, 2, figsize=(12, 8))
axes = axes.flatten()for i, feature in enumerate(selected_features):axes[i].hist(X_cluster4[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高血糖,胸痛类型为2,血管情况良好,年龄中等,心率和血压中等
#簇1空腹血糖正常,胸痛类型为3,血管情况良好,年龄中等,心率较高,血压中等
#簇2空腹血糖正常,胸痛类型为0,血管情况复杂,年龄中等,心率和血压中等
#簇3空腹血糖正常,胸痛类型为0和2,血管情况良好,年龄分布广,心率和血压中等
#簇4空腹血糖正常,胸痛类型为0和1,血管情况良好,年龄较轻,心率较高,血压中等
#簇0高血糖/非典型胸痛;簇1低风险/非典型胸痛;簇2高风险/典型心绞痛;簇3中风险/典型心绞痛;簇4年轻/低风险/多样症状
聚类构建新特征可以帮助疾病亚型诊断,并可能通过亚型作为新特征提高模型精度,一举两得。
@浙大疏锦行