DAY 19 特征筛选算法

数据预处理+划分数据集

# 先运行之前预处理好的代码
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%测试集

默认参数随机森林模型 rf_model

from sklearn.ensemble import RandomForestClassifier #随机森林分类器
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标

from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵
import warnings #用于忽略警告信息
warnings.filterwarnings("ignore") # 忽略所有警告信息

# --- 1. 默认参数的随机森林 ---
# 评估基准模型,这里确实不需要验证集
print("--- 1. 默认参数随机森林 (训练集 -> 测试集) ---")
import time # 这里介绍一个新的库,time库,主要用于时间相关的操作,因为调参需要很长时间,记录下会帮助后人知道大概的时长
start_time = time.time() # 记录开始时间
rf_model = RandomForestClassifier(random_state=42)
rf_model.fit(X_train, y_train) # 在训练集上训练
rf_pred = rf_model.predict(X_test) # 在测试集上预测
end_time = time.time() # 记录结束时间

print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")
print("\n默认随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred))
print("默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred))

输出:

--- 1. 默认参数随机森林 (训练集 -> 测试集) ---
训练与预测耗时: 0.9291 秒

默认随机森林 在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.77      0.97      0.86      1059
           1       0.79      0.30      0.43       441

    accuracy                           0.77      1500
   macro avg       0.78      0.63      0.64      1500
weighted avg       0.77      0.77      0.73      1500

默认随机森林 在测试集上的混淆矩阵:
[[1023   36]
 [ 309  132]]

        过去电脑性能比较差,特征数目太多计算起来很慢。同时特征中可能存在很多冗余特征干扰解释性、存在噪声特征干扰精度。

        所以在面对高维特征的时候常常需要引入特征降维,我们之前课程中的项目的特征也就小几十个,不太需要做降维,对于某些特征较多的数据,如基因数据、微生物数据、传感器数据等,特征较多,所以会考虑特征降维。

一、特征降维

特征降维一般有2种策略:

1. 特征筛选:从n个特征中筛选出m个特征,比如方差筛选,剔除方差过小的特征;利用皮尔逊相关系数筛选;lasso筛选(lasso自带的系数可以理解为重要性)、利用树模型自带的重要性、shap重要性等筛选;特征递归方法

2. 特征组合:从n个特征中组合出m个特征,如pca等

今天这节先说一下特征筛选:

  1. 方差筛选
  2. 皮尔逊相关系数筛选
  3. lasso筛选
  4. 树模型重要性
  5. shap重要性
  6. 递归特征消除REF

二、特征筛选

 1、方差筛选

方差筛选是一种简单而有效的特征筛选方法。

        核心逻辑:特征的方差反映了数据的变化程度,方差很小的特征几乎没有变化,对模型的预测帮助不大。比如,一个特征的值在所有样本中几乎都一样(方差接近0),那么它对区分不同类别或预测结果几乎没有贡献。

        因此,方差筛选会设定一个方差阈值,剔除方差低于这个阈值的特征,保留那些变化较大的特征,从而减少特征数量,提高模型效率。

        这种方法特别适合处理高维数据,能快速去掉不重要的特征,但它不考虑特征与目标变量之间的关系,可能会误删一些低方差但有意义的特征。

最常用基础筛选方法

  • 无监督方式快速剔除低方差特征
  • 适合数据预处理阶段
  • 示例代码中已应用此方法
# print("--- 方差筛选 (Variance Threshold) --- ")

# 导入需要的工具库
from sklearn.feature_selection import VarianceThreshold
import time

start_time = time.time()

# 创建方差筛选器,设置方差阈值为0.01
# 阈值是指方差的最小值,低于这个值的特征会被删除(可以根据数据情况调整阈值)
selecter = VarianceThreshold(threshold=0.01)

# 对训练数据进行方差筛选,fit_transform会计算每个特征的方差并剔除不满足阈值的特征
X_train_var = selecter.fit_transform(X_train)

# 对测试数据应用同样的筛选规则,transform会直接用训练数据的筛选结果处理测试数据
X_test_var = selecter.transform(X_test)

# 获取被保留下来的特征名称
# selector.get_support()返回一个布尔值列表,表示哪些特征被保留,这个是selector这个实例化的类的一个方法
selected_feature_var = X_train.columns[selecter.get_support()].tolist()

print(f"方差筛选后保留的特征数量:{len(selected_feature_var)}")
print(f"保留的特征:{selected_feature_var}")

# 重新训练和预测模型
rf_model_var = RandomForestClassifier(random_state=42)
rf_model_var.fit(X_train_var, y_train)
rf_pred_var = rf_model_var.predict(X_test_var)

end_time = time.time()
print(f"训练与预测耗时:{end_time - start_time:.4f}秒。")

print(f"\n经过方差筛选后的随机森林模型的分类报告:")
print(classification_report(y_test, rf_pred_var))

# 混淆矩阵显示了真实标签和预测标签的对应情况
print(f"\n经过方差筛选后的随机森林模型的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_var))

输出:

方差筛选后保留的特征数量:21
保留的特征:['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_debt consolidation', 'Purpose_home improvements', 'Purpose_other']
训练与预测耗时:1.5683秒。

经过方差筛选后的随机森林模型的分类报告:
              precision    recall  f1-score   support

           0       0.77      0.97      0.86      1059
           1       0.80      0.29      0.42       441

    accuracy                           0.77      1500
   macro avg       0.78      0.63      0.64      1500
weighted avg       0.78      0.77      0.73      1500


经过方差筛选后的随机森林模型的混淆矩阵:
[[1028   31]
 [ 315  126]]

2、lasso筛选

        Lasso回归(Least Absolute Shrinkage and Selection Operator)是一种结合特征选择和模型训练的方法。

       核心逻辑:在进行线性回归的同时,通过引入L1正则化项(即惩罚项),强制将一些不重要特征的回归系数压缩到0,从而实现特征筛选。

        换句话说,Lasso会自动“挑选”对预测目标有贡献的特征(系数不为0),而剔除无关或冗余的特征(系数为0)。

这种方法特别适合处理高维数据,可以减少特征数量,提高模型的解释性和计算效率。

✅ 线性模型首选方法

  • 通过L1正则化自动特征选择
  • 兼具特征选择和正则化效果
  • 适合高维线性问题
# print("--- Lasso筛选 (L1正则化) --- ")
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import Lasso
import time

start_time = time.time()

# 使用Lasso回归进行特征筛选
# Lasso回归模型初始化
lasso = Lasso(alpha=0.01, random_state=42)  # 调整alpha值以控制稀疏性
selector = SelectFromModel(lasso)  # 创建基于模型的筛选器
selector.fit(X_train, y_train)  # 在训练集上训练并筛选特征
X_train_lasso = selector.transform(X_train)  # 转换训练集数据
X_test_lasso = selector.transform(X_test)  # 转换测试集集数据

# 获取被保留下来的特征名称
# selector.get_support()返回一个布尔值列表,表示哪些特征被保留,这个是selector这个实例化的类的一个方法
selected_feature_lasso = X_train.columns[selecter.get_support()].tolist()
print(f"方差筛选后保留的特征数量:{len(selected_feature_lasso)}")
print(f"保留的特征:{selected_feature_lasso}")

# 重新训练和预测模型
rf_model_lasso = RandomForestClassifier(random_state=42)
rf_model_lasso.fit(X_train_lasso, y_train)
rf_pred_lasso = rf_model_lasso.predict(X_test_lasso)

end_time = time.time()
print(f"训练与预测耗时:{end_time - start_time:.4f}秒。")

print(f"\n经过lasso筛选后的随机森林模型的分类报告:")
print(classification_report(y_test, rf_pred_lasso))
print(f"\n经过lasso筛选后的随机森林模型的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_lasso))

输出:

方差筛选后保留的特征数量:21
保留的特征:['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_debt consolidation', 'Purpose_home improvements', 'Purpose_other']
训练与预测耗时:0.7287秒。

经过lasso筛选后的随机森林模型的分类报告:
              precision    recall  f1-score   support

           0       0.77      0.94      0.85      1059
           1       0.70      0.34      0.45       441

    accuracy                           0.76      1500
   macro avg       0.74      0.64      0.65      1500
weighted avg       0.75      0.76      0.73      1500


经过lasso筛选后的随机森林模型的混淆矩阵:
[[995  64]
 [293 148]]

        这个时候要注意,lasso本质上是回归模型,实际上用这个方法来筛选也是用回归模型对分类问题建模结束了,然后打印特征重要度,它是把0和1目标变量视为连续值来进行回归的。

        效果会差一点,不符合逻辑,但是确实可以计算。

 3、树模型重要性

✅ 最常用监督式方法

  • 随机森林/XGBoost自带特征重要性评估
  • 直观易用,适合非线性关系
  • 示例代码的随机森林已包含该特性
# print("--- 树模型自带的重要性筛选 --- ")
from sklearn.feature_selection import SelectFromModel
import time

start_time = time.time()

# 使用随机森林的特征重要性进行特征筛选
rf_selector = RandomForestClassifier(random_state=42)
rf_selector.fit(X_train, y_train)

# 创建特征筛选器
selector = SelectFromModel(rf_selector, threshold="mean")  # 阈值设为平均重要性,可调整

X_train_rf = selector.transform(X_train)  # 转换训练集数据
X_test_rf = selector.transform(X_test)  # 转换测试集集数据

# 获取被保留下来的特征名称
# selector.get_support()返回一个布尔值列表,表示哪些特征被保留,这个是selector这个实例化的类的一个方法
selected_feature_rf = X_train.columns[selecter.get_support()].tolist()
print(f"树模型重要性筛选后保留的特征数量:{len(selected_feature_rf)}")
print(f"保留的特征:{selected_feature_rf}")

# 重新训练和预测模型
rf_model_rf = RandomForestClassifier(random_state=42)
rf_model_rf.fit(X_train_rf, y_train)
rf_pred_rf = rf_model_rf.predict(X_test_rf)

end_time = time.time()
print(f"训练与预测耗时:{end_time - start_time:.4f}秒。")

print(f"\n经过树模型重要性筛选后的随机森林模型的分类报告:")
print(classification_report(y_test, rf_pred_rf))
print(f"\n经过树模型重要性筛选后的随机森林模型的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_rf))

输出:

树模型重要性筛选后保留的特征数量:21
保留的特征:['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_debt consolidation', 'Purpose_home improvements', 'Purpose_other']
训练与预测耗时:3.8901秒。

经过树模型重要性筛选后的随机森林模型的分类报告:
              precision    recall  f1-score   support

           0       0.76      0.97      0.85      1059
           1       0.79      0.27      0.40       441

    accuracy                           0.76      1500
   macro avg       0.78      0.62      0.63      1500
weighted avg       0.77      0.76      0.72      1500


经过树模型重要性筛选后的随机森林模型的混淆矩阵:
[[1027   32]
 [ 321  120]]

4、递归特征消除REF

✅ 常用但计算成本较高

  • 通过迭代寻找最优特征子集
  • 效果稳定但耗时长 
  • 适合中小规模数据集
# print("--- 递归特征消除 (RFE) --- ")
from sklearn.feature_selection import RFE
import time

start_time = time.time()

# 使用随机森林作为基础模型 进行REF
base_model = RandomForestClassifier(random_state=42)
rfe = RFE(base_model, n_features_to_select=10)  # 选择10个特征,可调整
rfe.fit(X_train, y_train)

X_train_rfe = selector.transform(X_train)  # 转换训练集数据
X_test_rfe = selector.transform(X_test)  # 转换测试集集数据

# 获取被保留下来的特征名称
# selector.get_support()返回一个布尔值列表,表示哪些特征被保留,这个是selector这个实例化的类的一个方法
selected_feature_rfe = X_train.columns[rfe.support_].tolist()
print(f"RFE筛选后保留的特征数量:{len(selected_feature_rfe)}")
print(f"保留的特征:{selected_feature_rfe}")

# 重新训练和预测模型
rf_model_rfe = RandomForestClassifier(random_state=42)
rf_model_rfe.fit(X_train_rfe, y_train)
rf_pred_rfe = rf_model_rfe.predict(X_test_rfe)

end_time = time.time()
print(f"训练与预测耗时:{end_time - start_time:.4f}秒。")

print(f"\n经过RFE筛选后的随机森林模型的分类报告:")
print(classification_report(y_test, rf_pred_rfe))
print(f"\n经过RFE筛选后的随机森林模型的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_rfe))

输出:

RFE筛选后保留的特征数量:10
保留的特征:['Id', 'Annual Income', 'Number of Open Accounts', 'Years of Credit History', 'Maximum Open Credit', 'Months since last delinquent', 'Current Loan Amount', 'Current Credit Balance', 'Monthly Debt', 'Credit Score']
训练与预测耗时:40.3209秒。

经过RFE筛选后的随机森林模型的分类报告:
              precision    recall  f1-score   support

           0       0.77      0.94      0.84      1059
           1       0.67      0.31      0.42       441

    accuracy                           0.75      1500
   macro avg       0.72      0.62      0.63      1500
weighted avg       0.74      0.75      0.72      1500


经过RFE筛选后的随机森林模型的混淆矩阵:
[[991  68]
 [304 137]]

5、皮尔逊相关系数筛选

        皮尔逊相关系数筛选是一种基于特征与目标变量之间相关性的特征选择方法。

        核心逻辑:计算每个特征与目标变量之间的相关系数(范围在-1到1之间,值越大表示正相关越强,值越小表示负相关越强,接近0表示几乎无关),然后根据相关系数的绝对值大小选择与目标变量相关性较高的特征,剔除相关性较低的特征。这种方法适用于目标变量是连续型的情况(如果是分类问题,可以先对目标变量编码)。

        通过皮尔逊相关系数筛选,我们可以保留那些对预测目标最有帮助的特征,减少无关或冗余特征的干扰。

        皮尔逊相关系数筛选法是一种基于变量相关性的经典特征选择技术,常用于处理目标变量为连续型的场景。若面对分类问题,通常需要先对目标变量进行编码处理,将其转化为数值型数据后再开展分析。

print("--- 皮尔逊相关系数筛选 ---")
from sklearn.feature_selection import SelectKBest, f_classif
import time

start_time = time.time()

# 计算特征与目标变量的相关性,选择前k个特征(这里设为10个,可调整)
# 注意:皮尔逊相关系数通常用于回归问题(连续型目标变量),但如果目标是分类问题,可以用f_classif
k = 10
selector = SelectKBest(score_func=f_classif, k=k)
X_train_corr = selector.fit_transform(X_train, y_train)
X_test_corr = selector.transform(X_test)

# 获取筛选后的特征名
selected_features_corr = X_train.columns[selector.get_support()].tolist()
print(f"皮尔逊相关系数筛选后保留的特征数量: {len(selected_features_corr)}")
print(f"保留的特征: {selected_features_corr}")

# 训练随机森林模型
rf_model_corr = RandomForestClassifier(random_state=42)
rf_model_corr.fit(X_train_corr, y_train)
rf_pred_corr = rf_model_corr.predict(X_test_corr)

end_time = time.time()
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")
print("\n皮尔逊相关系数筛选后随机森林在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_corr))
print("皮尔逊相关系数筛选后随机森林在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_corr))

输出:

--- 皮尔逊相关系数筛选 ---
皮尔逊相关系数筛选后保留的特征数量: 10
保留的特征: ['Id', 'Home Ownership', 'Annual Income', 'Tax Liens', 'Number of Open Accounts', 'Long Term', 'Current Loan Amount', 'Credit Score', 'Purpose_business loan', 'Purpose_small business']
训练与预测耗时: 1.3122 秒

皮尔逊相关系数筛选后随机森林在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.77      0.94      0.84      1059
           1       0.67      0.31      0.42       441

    accuracy                           0.75      1500
   macro avg       0.72      0.62      0.63      1500
weighted avg       0.74      0.75      0.72      1500

皮尔逊相关系数筛选后随机森林在测试集上的混淆矩阵:
[[991  68]
 [304 137]]

 6、shap重要性

print("--- SHAP重要性筛选 ---")
import shap
from sklearn.feature_selection import SelectKBest
import time

start_time = time.time()

# 使用随机森林模型计算SHAP值
rf_shap = RandomForestClassifier(random_state=42)
rf_shap.fit(X_train, y_train)
explainer = shap.TreeExplainer(rf_shap)
shap_values = explainer.shap_values(X_train)

# 计算每个特征的平均SHAP值(取绝对值的平均)
mean_shap = np.abs(shap_values[1]).mean(axis=0)  # shap_values[1]对应正类
k = 10  # 选择前10个特征,可调整
top_k_indices = np.argsort(mean_shap)[-k:]
X_train_shap = X_train.iloc[:, top_k_indices]
X_test_shap = X_test.iloc[:, top_k_indices]

# 获取筛选后的特征名
selected_features_shap = X_train.columns[top_k_indices].tolist()
print(f"SHAP重要性筛选后保留的特征数量: {len(selected_features_shap)}")
print(f"保留的特征: {selected_features_shap}")

# 训练随机森林模型
rf_model_shap = RandomForestClassifier(random_state=42)
rf_model_shap.fit(X_train_shap, y_train)
rf_pred_shap = rf_model_shap.predict(X_test_shap)

end_time = time.time()
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")
print("\nSHAP重要性筛选后随机森林在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_shap))
print("SHAP重要性筛选后随机森林在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_shap))

输出:

--- SHAP重要性筛选 ---
SHAP重要性筛选后保留的特征数量: 2
保留的特征: ['Home Ownership', 'Id']
训练与预测耗时: 427.9936 秒

SHAP重要性筛选后随机森林在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.70      0.74      0.72      1059
           1       0.29      0.25      0.27       441

    accuracy                           0.60      1500
   macro avg       0.50      0.50      0.50      1500
weighted avg       0.58      0.60      0.59      1500

SHAP重要性筛选后随机森林在测试集上的混淆矩阵:
[[785 274]
 [330 111]]

注:上面这些方法的计算耗时没意义,目的是筛选出最后用的特征,可以看到只保留这几个特征效果仍然很好。说明可以在未来的建模中减少计算资源。

建议实际工作流:方差筛选(快速初筛) → Lasso/树模型(精筛) → RFE(最终优化)

@浙大疏锦行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值