digital medicine复现:缺失值过滤、方差过滤、缺失值插补、相关系数与逐步特征选择

向量数据库大模型机器学习

picture.image

✨ 欢迎关注 ✨

本节介绍: 缺失值过滤、方差过滤、缺失值插补、相关系数与逐步特征选择 ,作者根据个人对机器学习的理解进行代码实现与图表输出,仅供参考。 完整 数据和代码获取,需要的朋友可关注公众文末提供的获取方式- Python项目代码文档合集 。 获取 前请咨询,避免不必要的问题。 文末点赞、推荐参与免费书籍包邮赠送《R语言数据分析 从入门到实践》!

✨ 文献信息 ✨

picture.image

缺失值处理:通过计算缺失率,剔除缺失率高于30%的变量(共删除了23个变量)。剩下了70个变量,方差过滤:对剩下的70个变量进行方差过滤,剔除方差过小的特征,最终保留61个变量,插补缺失值:采用多重插补法(使用逻辑回归模型)来处理剩余的缺失值(当然这里进行了多种插补方法比较),相关性过滤:计算变量之间的相关系数,剔除那些相关系数过高(大于0.7)的变量,最终保留了50个变量,逐步特征选择:在50个保留下来的变量中,进一步通过熵值计算选择最终的最优特征。最终,通过逐步特征选择,确定34个最优的特征(例如:年龄、外科历史、温度、血压等)让模型AUC值达到最优

picture.image

接下来在模拟数据集上执行类似的特征处理流程,并实现与该文献中描述的过程类似的步骤,可以参考以下流程。虽然这不是完全一样的过程,但核心思路和步骤是相似的。将在模拟数据集上进行该预处理过程,涉及缺失值处理、方差过滤、插补、相关性过滤和逐步特征选择

✨ 基础代码 ✨

  
import pandas as pd  
import numpy as np  
import matplotlib.pyplot as plt  
plt.rcParams['font.family'] = 'Times New Roman'  
plt.rcParams['axes.unicode_minus'] = False  
import warnings  
# 忽略所有警告  
warnings.filterwarnings("ignore")  
  
path = r"2025-8-8公众号Python机器学习AI.xlsx"  
df = pd.read_excel(path)  
X = df.drop(['Electrical_cardioversion'], axis=1)    
missing_data = X.isnull().sum()  
missing_data_percentage = (missing_data / len(df)) * 100  
  
# 创建一个新的DataFrame来显示缺失值的数量和占比  
missing_df = pd.DataFrame({  
    'Feature': missing_data.index,  
    'Missing Values': missing_data.values,  
    'Missing Percentage': missing_data_percentage.values  
})  
# 过滤掉没有缺失值的特征,并按缺失值数量降序排列  
missing_df = missing_df[missing_df['Missing Values'] > 0].sort_values(by='Missing Values', ascending=False)  
# 找出缺失值占比超过30%的特征  
high_missing_features = missing_df[missing_df['Missing Percentage'] > 30]['Feature'].tolist()  
# 打印出缺失值占比超过30%的特征名  
print(high_missing_features)  
missing_df

picture.image

读取Excel文件,计算并展示数据集中各个特征的缺失值数量和缺失百分比,过滤出缺失值占比超过30%的特征并打印出来

  
X = X.drop(columns=high_missing_features)  
missing_data = X.isnull().sum()  
missing_data = missing_data[missing_data > 0]  
print(missing_data)

删除缺失值占比超过30%的特征,并打印出剩余特征中仍然存在缺失值的特征及其缺失数量

  
BMI            9  
SystolicBP    11  
Pulse          5  
Hb             3  
FT3            1  
HbA1c          1
  
from sklearn.feature_selection import VarianceThreshold  
  
# 连续性特征列表  
num_vars = [  
    'Age', 'BMI', 'CHA2DS2VASC', 'AFCourse', 'SurgeryTime',   
    'LeftAtrialDiam', 'SystolicBP', 'DiastolicBP', 'Pulse',   
    'AST', 'NtproBNP', 'Cr', 'Hb', 'TSH', 'FT4', 'FT3',   
    'HbA1c', 'EF'  
]  
  
# 选择需要进行方差计算的连续性特征  
continuous_vars = X[num_vars]  
  
# 初始化VarianceThreshold并设定阈值为0.90*(1-0.90)  
variance_threshold = VarianceThreshold(threshold=(0.90 * (1 - 0.90)))  
  
# 进行方差选择  
continuous_vars_selected = variance_threshold.fit_transform(continuous_vars)  
  
# 获取保留下来的特征名称  
selected_feature_names = continuous_vars.columns[variance_threshold.get_support()]  
selected_feature_names

计算连续性特征的方差,并使用方差阈值过滤掉方差过小的特征,保留方差大于设定阈值的特征,最后输出保留下来的特征名称

文献中说明是对布尔特征进行方差剔除,认为是考虑到如果某个特征在整个数据集中都是常量(例如,全部为 0 或全部为 1),那么它的方差为 0,因此这样的特征应该被移除。对于布尔特征,计算方差有助于识别这种情况,而对于连续性变量,方差值能够更好地反映特征的变异性,进而帮助选择最有信息量的特征,所以这里是对连续变量处理,模拟数据布尔特征并不存全部为0或1在这种情况

  
Index(['Age', 'BMI', 'CHA2DS2VASC', 'AFCourse', 'SurgeryTime',         'LeftAtrialDiam', 'SystolicBP', 'DiastolicBP', 'Pulse', 'AST',         'NtproBNP', 'Cr', 'Hb', 'TSH', 'FT4', 'FT3'],  
      dtype='object')
  
# 获取被剔除的特征名称  
removed_feature_names = continuous_vars.columns[~variance_threshold.get_support()]  
removed_feature_names

获取被剔除的特征名称,即方差小于设定阈值的特征

  
Index(['HbA1c', 'EF'], dtype='object')
  
X = X.drop(columns=removed\_feature\_names)

最后删除方差小于设定阈值的特征(即removed_feature_names中列出的特征),接下来进行缺失值插补

  
from sklearn.model_selection import GridSearchCV  
from sklearn.impute import KNNImputer  
# 定义参数网格  
param_grid = {  
    'n_neighbors': np.arange(10, 200, 10),  # 邻居数范围  
    'weights': ['uniform', 'distance'],     # 权重选择  
}  
# 实例化 KNNImputer  
imputer_knn = KNNImputer()  
# 使用 GridSearchCV 进行网格搜索  
grid_search = GridSearchCV(imputer_knn, param_grid, cv=5, scoring='roc_auc', n_jobs=-1)  
grid_search.fit(X[num_vars], df['Electrical_cardioversion'])  
best_params = grid_search.best_params_  
print("Best parameters found: ", best_params)

使用KNNImputer和GridSearchCV进行超参数调优,选择最优的超参数组合来填补数据中的缺失值

  
Best parameters found:  {'n\_neighbors': 10, 'weights': 'uniform'}
  
# 使用最佳参数初始化 KNNImputer  
imputer_best_knn = KNNImputer(n_neighbors=best_params['n_neighbors'], weights=best_params['weights'])  
  
# 用 KNNImputer 对 `X[num_vars]` 填补缺失值  
X_imputed = imputer_best_knn.fit_transform(X[num_vars])  
X_imputed_knn = pd.DataFrame(X_imputed, columns=X[num_vars].columns)  
cat_vars = [  
    'Sex', 'EarlyRelapse', 'LateRelapse', 'HF', 'HTN', 'DM',   
    'CerebralInfarction', 'CoronaryHD', 'AtrialFibrillationType',   
    'AF6Y', 'Cardiomyopathy', 'SleepApnea', 'RenalInsuff',   
    'Hyperlipidemia', 'COPD', 'HeartValveDisease', 'SmokingHistory',   
    'DrinkingHistory', 'SGLT2i', 'Statin', 'B', 'Amiodarone',   
    'Propafenone', 'Rivaroxaban', 'Dabigatran', 'ACEI_ARB_ARNI'  
]  
  
X_imputed_knn['Electrical_cardioversion'] = df['Electrical_cardioversion']  
X_imputed_knn = pd.concat([X_imputed_knn, df[cat_vars]], axis=1)

使用在网格搜索中找到的最佳超参数初始化KNNImputer,用来填补X[num_vars]中的缺失值,并将填补后的数据与原始的分类特征(cat_vars)合并,最终返回填补后的完整数据集

这里都是对连续性变量(num_vars)进行缺失值插补,而分类性变量(cat_vars)没有缺失值,因此未进行插补

  
from sklearn.model_selection import train_test_split  
from sklearn.ensemble import RandomForestClassifier  
from sklearn.metrics import roc_auc_score  
# 划分特征和目标变量  
X_knn = X_imputed_knn.drop(['Electrical_cardioversion'], axis=1)    
y_knn = X_imputed_knn['Electrical_cardioversion']    
# 划分训练集和测试集  
X_train_knn, X_test_knn, y_train_knn, y_test_knn = train_test_split(  
    X_knn,    
    y_knn,   
    test_size=0.3,   
    random_state=250807,   
    stratify=X_imputed_knn['Electrical_cardioversion']   
)  
  
# 默认参数下的随机森林模型  
rf_knn = RandomForestClassifier(random_state=42)    
rf_knn.fit(X_train_knn, y_train_knn)  
# 在测试集上进行预测  
y_pred_prob = rf_knn.predict_proba(X_test_knn)[:, 1]  # 获取预测的概率值  
# 计算AUC值  
auc_score = roc_auc_score(y_test_knn, y_pred_prob)  
print("AUC: ", auc_score)

使用插补后的数据训练一个默认参数的随机森林分类器,计算并输出模型在测试集上的AUC值,用于评估模型的预测性能

  
AUC:  0.9092307692307692

为什么这里要使用插补后的数据进行一个默认的机器学习模型构建,是因为后续还会采用另外三种方法进行填补(线性回归多重插补、k近邻多重插补、均值+岭回归插补),结合默认的模型训练可以反馈对比哪一种插补方法更好

picture.image

最后可以绘制不同插补方法的ROC曲线,通过比较测试集上的AUC值来突出显示最佳插补模型(即AUC最大的模型)。灰色的虚线代表其他插补方法的ROC曲线,而橙色的实线代表AUC值最大的插补方法的ROC曲线,可以发现在模拟数据集上插补最好的方法是 k近邻多重插补,所以接下来处理的数据是经过 k近邻多重插补后的数据集

  
import seaborn as sns  
#计算相关系数矩阵  
correlation_matrix = X_imputed_knn_iteration[num_vars].corr()  
plt.figure(figsize=(12, 8), dpi=1200)  
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm_r', fmt='.2f', linewidths=0.5, cbar_kws={"shrink": 0.8})  
plt.savefig("Heatmap.pdf", format='pdf', bbox_inches='tight',dpi=1200)  
plt.show()

picture.image

可视化插补后数据中连续性变量的相关系数矩阵

  
# 找出相关系数绝对值大于或等于0.7的特征对  
high_corr_vars = np.where(correlation_matrix.abs() >= 0.7)  
  
# 获取特征对(排除重复和自己与自己的相关)  
high_corr_pairs = [(correlation_matrix.index[x], correlation_matrix.columns[y])   
                   for x, y in zip(*high_corr_vars)   
                   if x != y and x < y]  
  
print("相关系数绝对值大于或等于0.7的特征对:")  
print(high_corr_pairs)

找出相关系数绝对值大于或等于0.7的特征对(这是一个阈值和文献一样,表示高度相关变量组需要进行剔除),并排除重复和自己与自己的相关也就是对角线

  
相关系数绝对值大于或等于0.7的特征对:  
[('FT4', 'FT3')]

在结合相关性特征选择的过程中,选择使得AUC值更高的特征并剔除使AUC值较低的特征

picture.image

最终选择了保留FT4这个特征剔除FT3,如果存在多个高度相关特征对组合,处理方法类似

  
X\_corr = X\_imputed\_knn\_iteration.drop(['FT3'], axis=1)  

接下来就可以进行熵值的计算,在进行熵值计算前需要了解什么是熵值,熵越大,表示信息量越大,不确定性越高。若一个特征的取值非常集中(如所有样本都是某个特定值),则熵较低,表示这个特征的区分能力弱;如果特征值均匀分布,则熵较高,表示该特征的区分能力强,对于连续性变量,直接计算熵值是困难的,因为连续值有无限的可能性,无法像离散值那样直接统计概率。因此,对连续变量进行分箱(binning)是常见的预处理方法,通过分箱,连续性变量就变成了离散化的特征,可以像离散变量一样进行熵值计算

  
def calculate_entropy(series, bins=20):  
    """计算熵值,适用于分类变量和连续变量"""  
  
    if series.dtype == 'object' or series.nunique() < 10:  # 分类变量  
        value_counts = series.value_counts(normalize=True, dropna=False)  
        entropy = -(value_counts * np.log2(value_counts + 1e-10)).sum()  
    else:  # 连续变量  
        # 对连续变量进行分箱  
        binned = pd.cut(series, bins=bins, include_lowest=True)  
        value_counts = binned.value_counts(normalize=True, dropna=False)  
        entropy = -(value_counts * np.log2(value_counts + 1e-10)).sum()  
  
    return entropy  
  
X_corr_copy = X_corr.drop(columns=['Electrical_cardioversion'])  
  
# 计算 num_vars 和 cat_vars 中每个特征的熵值  
feature_entropies = X_corr_copy.apply(calculate_entropy, bins=20)  
# 将结果转换为 DataFrame 并排序  
entr_result = pd.DataFrame(feature_entropies).reset_index()  
entr_result.columns = ['Feature', 'Entropy']  
entr_result = entr_result.sort_values(by='Entropy', ascending=False)  
entr_result.head()

picture.image

通过计算每个特征的熵值(分类变量直接计算,连续变量先进行分箱处理),并将熵值按降序排列,最终输出包含特征名称和对应熵值的DataFrame

picture.image

最后根据熵值从高到低对特征进行排序,选择前N个特征作为最重要的特征,通过逐步增加特征数量,并计算模型的AUC,最终选择使AUC达到最高的特征子集,在这里的模拟数据上,选择前22个特征AUC达到最高0.9277

当然,公众号中还有更多机器学习期刊实战技巧,您可以通过历史文章进行检索和阅读,关注公众号,点击“发信息”>“历史文章”即可搜索公众号所有文章信息

picture.image

✨ 该文章案例 ✨

picture.image

在上传至交流群的文件中,像往期文章一样,将对案例进行逐步分析,确保读者能够达到最佳的学习效果。内容都经过详细解读,帮助读者深入理解模型的实现过程和数据分析步骤,从而最大化学习成果。

同时,结合提供的免费AI聚合网站进行学习,能够让读者在理论与实践之间实现融会贯通,更加全面地掌握核心概念。

✨ 介绍 ✨

本节介绍到此结束,有需要学习数据分析和Python机器学习相关的朋友欢迎到淘宝店铺:Python机器学习AI,下方提供淘宝店铺二维码获取作者的公众号合集。截至目前为止,合集已包含近300多篇文章,购买合集的同时,还将提供免费稳定的AI大模型使用。

更新的内容包含数据、代码、注释和参考资料。 作者仅分享案例项目,不提供额外的答疑服务。项目中将提供详细的代码注释和丰富的解读,帮助您理解每个步骤 。 获取 前请咨询,避免不必要的问题。

✨ 群友反馈 ✨

picture.image

✨ 淘宝店铺 ✨

picture.image

请大家打开淘宝扫描上方的二维码,进入店铺,获取更多Python机器学习和AI相关的内容 ,希望能为您的学习之路提供帮助!

✨ 免费赠书 ✨

picture.image

picture.image

支持知识分享,畅享学习乐趣!特别感谢清华大学出版社 对本次赠书活动的鼎力支持!即日起,只需点赞、推荐、转发 此文章,作者将从后台随机抽取一位幸运儿,免费包邮赠送清华出版社提供的《R语言数据分析从入门到实践》这本精彩书籍📚!

💡 赶快参与,一键三连,说不定你就是那位幸运读者哦!

往期推荐

期刊复现:连续数据与分类数据共存的SHAP可视化散点图与箱形图组合形式

期刊复现:多分类任务如何拆分为二分类任务并进行堆叠预测提高模型预测性能

期刊配图:SHAP模型解释多种特征重要性柱状图可视化解析

期刊配图:SHAP值分析模型可解释性在柱状图与蜂窝图中的进阶组合展示

期刊配图:通过堆叠Mean|SHAP|展示不同区间对模型贡献度的可视化分析

期刊复现:利用UMAP降维算法可视化深度学习随着训练次数的增加模型区分能力的变化

期刊配图:PCA、t-SNE与UMAP三种降维方法简化高维数据的展示应用对比

Science期刊复现:分类、回归与Shap分析多角度揭示同一数据集变量对目标的影响

多模型SHAP+PDP解读Stacking集成模型:从基学习器到元学习器的可解释性与模型调参实现

picture.image

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

个人观点,仅供参考

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
火山引擎大规模机器学习平台架构设计与应用实践
围绕数据加速、模型分布式训练框架建设、大规模异构集群调度、模型开发过程标准化等AI工程化实践,全面分享如何以开发者的极致体验为核心,进行机器学习平台的设计与实现。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论