目录
一、基于聚类进一步推断类型
选用昨天kmeans得到的效果进行聚类,进而推断出每个簇的实际含义。
1. 聚类分析
# 先运行之前预处理好的代码
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('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("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%测试集
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 = 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())
k=2, 惯性: 218529.50, 轮廓系数: 0.320, CH 指数: 479.34, DB 指数: 3.222 k=3, 惯性: 207982.87, 轮廓系数: 0.209, CH 指数: 441.88, DB 指数: 2.906 k=4, 惯性: 200477.28, 轮廓系数: 0.220, CH 指数: 399.12, DB 指数: 2.441 k=5, 惯性: 192940.36, 轮廓系数: 0.224, CH 指数: 384.19, DB 指数: 2.042 k=6, 惯性: 185411.81, 轮廓系数: 0.227, CH 指数: 380.64, DB 指数: 1.733 k=7, 惯性: 178444.49, 轮廓系数: 0.130, CH 指数: 378.31, DB 指数: 1.633 k=8, 惯性: 174920.27, 轮廓系数: 0.143, CH 指数: 352.31, DB 指数: 1.817 k=9, 惯性: 167383.96, 轮廓系数: 0.150, CH 指数: 364.27, DB 指数: 1.636 k=10, 惯性: 159824.84, 轮廓系数: 0.156, CH 指数: 378.43, DB 指数: 1.502
KMeans Cluster labels (k=3) added to X: KMeans_Cluster 0 5205 1 1381 2 914 dtype: int64
现在需要给这个簇赋予实际的含义,一般当你赋予实际含义的时候,你需要根据某几个特征来赋予,但是源数据特征很多,如何选择特征呢?有2种思路:
-
你最开始聚类的时候,就选择了你想最后用来确定簇含义的特征,那么你需要选择一些特征来进行聚类,那么你最后确定簇含义的特征就是这几个特征,而非全部。如你想聚类消费者购买习惯,那么他过去的消费记录、购买记录、购买金额等等,这些特征都与消费者购买习惯有关,你可以使用这些特征来确定簇含义,一些其他的特征,如消费者年龄,工作行业则不考虑。----适用于你本身就有构造某些明确含义的特征的情况。
-
最开始用全部特征来聚类,把其余特征作为 x,聚类得到的簇类别作为标签构建监督模型,进而根据重要性筛选特征,来确定要根据哪些特征赋予含义。---使用于你想构造什么,目前还不清楚。
【上面这个思路好好学,这是真干货,构造具有价值的特征工程,妥妥的创新点和工作量】
X.columns
Index(['Id', 'Home Ownership', 'Annual Income', 'Years in current job', 'Tax Liens', 'Number of Open Accounts', 'Years of Credit History', 'Maximum Open Credit', 'Number of Credit Problems', 'Months since last delinquent', 'Bankruptcies', 'Long Term', 'Current Loan Amount', 'Current Credit Balance', 'Monthly Debt', 'Credit Score', 'Purpose_business loan', 'Purpose_buy a car', 'Purpose_buy house', 'Purpose_debt consolidation', 'Purpose_educational expenses', 'Purpose_home improvements', 'Purpose_major purchase', 'Purpose_medical bills', 'Purpose_moving', 'Purpose_other', 'Purpose_renewable energy', 'Purpose_small business', 'Purpose_take a trip', 'Purpose_vacation', 'Purpose_wedding', 'KMeans_Cluster'], dtype='object')
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) # 训练模型,此时无需在意准确率 直接全部数据用来训练了
RandomForestClassifier(random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
shap.initjs()
# 初始化 SHAP 解释器
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(x1) # 这个计算耗时
shap_values.shape # 第一维是样本数,第二维是特征数,第三维是类别数
(7500, 31, 3)
# --- 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()
--- 1. SHAP 特征重要性条形图 ---
# 此时判断一下这几个特征是离散型还是连续型
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} 可能是连续型变量')
Purpose_debt consolidation 的唯一值数量: 2 Purpose_debt consolidation 可能是离散型变量 Bankruptcies 的唯一值数量: 5 Bankruptcies 可能是离散型变量 Number of Credit Problems 的唯一值数量: 8 Number of Credit Problems 可能是离散型变量 Purpose_other 的唯一值数量: 2 Purpose_other 可能是离散型变量
# 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()
KMeans_Cluster 0 5205 1 1381 2 914 dtype: int64
# 分别筛选出每个簇的数据
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()
把这三个簇的图发给ai,让ai给你定义
2. 簇的总结与定义
- 第一个簇 - 优质信用稳健财务型
- 特征总结:在债务合并用途上表现一致,几乎无破产记录,信用问题极少,资金用途集中且很少涉及特殊类别。财务状况稳定,信用良好,资金流向明确。
- 定义依据:各项关键财务和信用指标表现优异,显示出良好的财务自律性和信用履约能力,所以定义为 “优质信用稳健财务型”。
- 第二个簇 - 较稳健但信用有分化财务型
- 特征总结:多数无债务合并需求,破产情况少见,但信用问题上存在个体差异,资金用途有一定分散性。整体财务状况相对稳定,但在信用和资金使用方向上不如第一个簇表现一致。
- 定义依据:虽然总体财务状况尚可,但信用记录和资金用途的分化情况使其区别于第一个簇,因此定义为 “较稳健但信用有分化财务型”。
- 第三个簇 - 高风险财务困境型
- 特征总结:债务合并需求分化,破产经历较多,信用问题普遍且严重,资金用途有差异。在财务健康和信用方面存在诸多问题,面临较大财务风险。
- 定义依据:基于明显的财务困境迹象和高风险特征,定义为 “高风险财务困境型”。
现在你就得到了一个全新的特征,完成了你的特征工程
后续研究需要对这个特征独热编码,然后重新建模训练,如果加了这个特征后模型精度提高,说明这个特征是有用的。
二、作业
参考示例代码对心脏病数据集采取类似操作,并且评估特征工程后模型效果有无提升。
1. 聚类分析
# 环境设置
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score # 添加缺失的导入
import shap
import warnings
warnings.filterwarnings("ignore")
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 数据加载与预处理
def load_and_preprocess():
# 加载数据
df = pd.read_csv('heart.csv')
# 显示列名用于调试
print("数据集列名:", df.columns.tolist())
# 自动识别目标变量(假设最后一列为目标变量)
target_col = df.columns[-1]
print(f"\n自动识别目标变量: {target_col}")
# 处理分类变量(自动检测)
categorical_cols = df.select_dtypes(include=['object', 'category']).columns.tolist()
print("\n检测到的分类变量:", categorical_cols)
# 标签编码
le = LabelEncoder()
for col in categorical_cols:
df[col] = le.fit_transform(df[col])
# 处理缺失值
df.fillna(df.median(numeric_only=True), inplace=True)
# 分离特征和目标变量
X = df.drop(target_col, axis=1)
y = df[target_col]
# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
return X_scaled, X, y, target_col
# 2. KMeans聚类分析
def perform_clustering(X_scaled):
# 寻找最优K值
inertia = []
silhouette = []
k_range = range(2, 8)
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42)
labels = kmeans.fit_predict(X_scaled)
inertia.append(kmeans.inertia_)
silhouette.append(silhouette_score(X_scaled, labels)) # 现在可以正常使用
# 可视化评估指标
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(k_range, inertia, 'bo-')
plt.title('肘部法则')
plt.xlabel('簇数量')
plt.subplot(1,2,2)
plt.plot(k_range, silhouette, 'ro-')
plt.title('轮廓系数')
plt.xlabel('簇数量')
plt.tight_layout()
plt.show()
# 选择K=3进行聚类
kmeans = KMeans(n_clusters=3, random_state=42)
clusters = kmeans.fit_predict(X_scaled)
return clusters
# 3. 特征重要性分析
def analyze_features(X, clusters):
# 添加聚类标签
X_clustered = X.copy()
X_clustered['Cluster'] = clusters
# 训练随机森林模型
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X, clusters)
# SHAP分析
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X)
# 特征重要性可视化
plt.figure(figsize=(10,6))
shap.summary_plot(shap_values, X, plot_type="bar", show=False)
plt.title('SHAP特征重要性')
plt.show()
return X_clustered
# 4. 聚类结果解释
def interpret_clusters(X_clustered):
# PCA可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_clustered.drop('Cluster', axis=1))
plt.figure(figsize=(8,6))
sns.scatterplot(x=X_pca[:,0], y=X_pca[:,1], hue=X_clustered['Cluster'],
palette='viridis', alpha=0.8)
plt.title('聚类结果可视化 (PCA降维)')
plt.xlabel('主成分1')
plt.ylabel('主成分2')
plt.show()
# 关键特征分析
print("\n各簇特征分布:")
print(X_clustered.groupby('Cluster').agg(['mean', 'median']).T)
# 5. 特征工程效果验证
def evaluate_improvement(X_clustered, y, target_col):
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 添加聚类特征
X_new = X_clustered.copy()
X_new = pd.get_dummies(X_new, columns=['Cluster'], prefix='Cluster')
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X_new, y, test_size=0.3, random_state=42)
# 基准模型
base_model = GradientBoostingClassifier(random_state=42)
base_model.fit(X_train.drop(columns=X_new.filter(regex='Cluster_').columns), y_train)
base_acc = accuracy_score(y_test, base_model.predict(X_test.drop(columns=X_new.filter(regex='Cluster_').columns)))
# 新模型
new_model = GradientBoostingClassifier(random_state=42)
new_model.fit(X_train, y_train)
new_acc = accuracy_score(y_test, new_model.predict(X_test))
print(f"\n基准模型准确率: {base_acc:.4f}")
print(f"新增特征后准确率: {new_acc:.4f}")
print(f"准确率提升: {(new_acc - base_acc):.4f}")
# 主流程
if __name__ == "__main__":
# 数据预处理
X_scaled, X, y, target_col = load_and_preprocess()
# 显示预处理后的数据
print("\n预处理后的数据样例:")
print(pd.DataFrame(X_scaled, columns=X.columns).head())
# 聚类分析
clusters = perform_clustering(X_scaled)
# 特征分析
X_clustered = analyze_features(X, clusters)
# 结果解释
interpret_clusters(X_clustered)
# 效果验证
evaluate_improvement(X_clustered, y, target_col)
输出结果:
数据集列名: ['age', 'sex', 'cp', 'trestbps', 'chol', 'fbs', 'restecg', 'thalach', 'exang', 'oldpeak', 'slope', 'ca', 'thal', 'target']
自动识别目标变量: target
检测到的分类变量: []
预处理后的数据样例:
age sex cp trestbps chol fbs restecg \
0 0.952197 0.681005 1.973123 0.763956 -0.256334 2.394438 -1.005832
1 -1.915313 0.681005 1.002577 -0.092738 0.072199 -0.417635 0.898962
2 -1.474158 -1.468418 0.032031 -0.092738 -0.816773 -0.417635 -1.005832
3 0.180175 0.681005 0.032031 -0.663867 -0.198357 -0.417635 0.898962
4 0.290464 -1.468418 -0.938515 -0.663867 2.082050 -0.417635 0.898962
thalach exang oldpeak slope ca thal
0 0.015443 -0.696631 1.087338 -2.274579 -0.714429 -2.148873
1 1.633471 -0.696631 2.122573 -2.274579 -0.714429 -0.512922
2 0.977514 -0.696631 0.310912 0.976352 -0.714429 -0.512922
3 1.239897 -0.696631 -0.206705 0.976352 -0.714429 -0.512922
4 0.583939 1.435481 -0.379244 0.976352 -0.714429 -0.512922
各簇特征分布:
Cluster 0 1 2
age mean 58.092784 58.150538 48.053097
median 59.000000 58.000000 48.000000
sex mean 0.814433 0.268817 0.911504
median 1.000000 0.000000 1.000000
cp mean 0.185567 1.419355 1.265487
median 0.000000 2.000000 1.000000
trestbps mean 135.876289 133.397849 126.513274
median 132.000000 132.000000 125.000000
chol mean 254.752577 257.817204 229.469027
median 254.000000 248.000000 229.000000
fbs mean 0.185567 0.182796 0.088496
median 0.000000 0.000000 0.000000
restecg mean 0.432990 0.440860 0.681416
median 0.000000 0.000000 1.000000
thalach mean 129.824742 150.516129 165.946903
median 131.000000 154.000000 168.000000
exang mean 0.731959 0.150538 0.123894
median 1.000000 0.000000 0.000000
oldpeak mean 1.965979 0.673118 0.546018
median 1.800000 0.400000 0.000000
slope mean 0.979381 1.430108 1.734513
median 1.000000 1.000000 2.000000
ca mean 1.381443 0.344086 0.486726
median 1.000000 0.000000 0.000000
thal mean 2.597938 1.956989 2.362832
median 3.000000 2.000000 2.000000
基准模型准确率: 0.7802
新增特征后准确率: 0.8132
准确率提升: 0.0330
2. 簇的总结与定义
(1)Cluster 0(紫色) - 高风险集中群体
分布特征:主成分1(+100 ~ +300)与主成分2(+20 ~ +60)双高区域
潜在特性:
- 可能具有高静息血压(RestingBP)与高胆固醇(Cholesterol)组合
- 年龄分布可能偏大(主成分1正方向常与年龄正相关)
- 最大心率(MaxHR)指标可能较低(与主成分2正相关呈反向趋势)
风险特征:心血管代谢指标异常集中,建议重点关注潜在冠心病高危人群
(2)Cluster 1(青色) - 中度风险过渡群体
分布特征:主成分1(-50 ~ +200)与主成分2(-20 ~ +40)广泛分布
潜在特性:
- 血压与胆固醇指标呈现中间值
- 可能包含非典型症状患者(ST段压低oldpeak值中等)
- 运动诱发心绞痛(exng)发生率具有波动性
风险特征:临床指标存在异质性,需结合其他诊断参数进行分层管理
(3)Cluster 2(黄色) - 低风险健康群体
分布特征:主成分1(-100 ~ +50)与主成分2(-60 ~ 0)双低区域
潜在特性:
- 静息血压与胆固醇水平最优
- 最大心率保持最佳生理范围
- 血管通畅度指标(caa)可能最优
风险特征:心血管系统功能指标良好,建议作为健康对照组重点研究对象
3. 模型效果提升
评估特征工程后模型效果有无提升
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score, roc_auc_score, f1_score
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
# 1. 数据加载与预处理
def load_data():
df = pd.read_csv('heart.csv')
# 自动识别目标变量(假设最后一列)
target_col = df.columns[-1]
print(f"目标变量: {target_col}")
# 处理分类变量
categorical_cols = df.select_dtypes(include=['object']).columns
le = LabelEncoder()
for col in categorical_cols:
df[col] = le.fit_transform(df[col])
# 填充缺失值
df.fillna(df.median(numeric_only=True), inplace=True)
# 分离特征和标签
X = df.drop(target_col, axis=1)
y = df[target_col]
return X, y, target_col
# 2. 特征工程(添加聚类特征)
def add_cluster_features(X):
# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# KMeans聚类
kmeans = KMeans(n_clusters=3, random_state=42)
clusters = kmeans.fit_predict(X_scaled)
# 添加聚类特征
X_new = X.copy()
X_new['Cluster'] = clusters
# PCA可视化验证
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
plt.figure(figsize=(8,6))
sns.scatterplot(x=X_pca[:,0], y=X_pca[:,1], hue=clusters,
palette='viridis', alpha=0.8)
plt.title('特征工程后的聚类分布')
plt.xlabel('主成分1')
plt.ylabel('主成分2')
plt.show()
return X_new
# 3. 模型评估对比
def evaluate_improvement(X_original, X_enhanced, y):
# 划分数据集
X_train_base, X_test_base, y_train, y_test = train_test_split(X_original, y, test_size=0.3, random_state=42)
X_train_enh, X_test_enh = train_test_split(X_enhanced, test_size=0.3, random_state=42)[:2]
# 基准模型
base_model = GradientBoostingClassifier(random_state=42)
base_model.fit(X_train_base, y_train)
base_pred = base_model.predict(X_test_base)
# 增强模型
enhanced_model = GradientBoostingClassifier(random_state=42)
enhanced_model.fit(X_train_enh, y_train)
enhanced_pred = enhanced_model.predict(X_test_enh)
# 指标对比
metrics = {
'准确率': (accuracy_score(y_test, base_pred), accuracy_score(y_test, enhanced_pred)),
'AUC': (roc_auc_score(y_test, base_model.predict_proba(X_test_base)[:,1]),
roc_auc_score(y_test, enhanced_model.predict_proba(X_test_enh)[:,1])),
'F1': (f1_score(y_test, base_pred), f1_score(y_test, enhanced_pred))
}
# 可视化对比
metrics_df = pd.DataFrame(metrics, index=['基准模型', '增强模型']).T
metrics_df.plot(kind='bar', figsize=(10,6), color=['skyblue', 'orange'])
plt.title('模型性能对比')
plt.ylabel('分数')
plt.xticks(rotation=0)
plt.ylim(0, 1.05)
plt.show()
return metrics_df
# 主流程
if __name__ == "__main__":
# 数据准备
X, y, target = load_data()
# 特征工程
X_enhanced = add_cluster_features(X)
# 性能评估
print("\n模型性能对比:")
results = evaluate_improvement(X, X_enhanced, y)
print(results)
# 效果总结
improvement = (results['增强模型'] - results['基准模型']).mean()
print(f"\n平均性能提升: {improvement:.2%}")
if improvement > 0.05:
print("✅ 特征工程显著提升模型性能")
elif improvement > 0:
print("⚠️ 特征工程带来小幅提升")
else:
print("❌ 特征工程未产生积极效果")
目标变量: target
模型性能对比:
基准模型 增强模型
准确率 0.780220 0.835165
AUC 0.870244 0.883902
F1 0.791667 0.842105
平均性能提升: 3.97%
⚠️ 特征工程带来小幅提升