别再只调参了!用Python的sklearn实战随机森林特征重要性,附完整代码与可视化
别再只调参了!用Python的sklearn实战随机森林特征重要性,附完整代码与可视化
在机器学习项目中,特征选择往往是被低估的关键环节。许多数据科学家花费大量时间调整模型参数,却忽视了更基础的问题:我们真的使用了正确的特征吗?本文将带你用scikit-learn 1.3+实战随机森林特征重要性分析,从数据预处理到可视化解读,提供可直接复用的代码方案。
1. 环境准备与数据加载
首先确保你的Python环境已安装以下库:
pip install scikit-learn==1.3.0 pandas matplotlib seaborn我们将使用经典的泰坦尼克数据集作为示例,这个数据集包含乘客特征与生存结果的关联,非常适合演示特征重要性分析:
import pandas as pd from sklearn.datasets import fetch_openml # 加载泰坦尼克数据集 titanic = fetch_openml('titanic', version=1, as_frame=True) df = titanic.data df['Survived'] = titanic.target # 查看特征示例 print(df[['pclass', 'sex', 'age', 'fare', 'embarked']].head())常见陷阱:直接使用原始数据会导致分析偏差。例如:
- 缺失值:年龄字段约20%缺失
- 类别特征:如embarked(登船港口)需要编码
- 数值尺度:票价(fare)范围远大于年龄(age)
2. 数据预处理与特征工程
2.1 处理缺失值与类别特征
from sklearn.impute import SimpleImputer from sklearn.preprocessing import OneHotEncoder from sklearn.compose import ColumnTransformer # 定义预处理流程 numeric_features = ['age', 'fare'] numeric_transformer = SimpleImputer(strategy='median') categorical_features = ['embarked', 'sex', 'pclass'] categorical_transformer = OneHotEncoder(handle_unknown='ignore') preprocessor = ColumnTransformer( transformers=[ ('num', numeric_transformer, numeric_features), ('cat', categorical_transformer, categorical_features) ]) # 应用预处理 X = preprocessor.fit_transform(df) y = df['Survived'].astype(int)提示:高基数类别特征(如超过100个类别)会扭曲重要性评估,建议先进行分桶处理
2.2 特征名称重建
预处理后特征名称会变化,我们需要重建它们以便后续分析:
# 获取OneHot编码后的特征名 cat_encoder = preprocessor.named_transformers_['cat'] new_cat_features = cat_encoder.get_feature_names_out(categorical_features) # 合并所有特征名 all_features = numeric_features + list(new_cat_features) print(f"最终特征列表:{all_features}")3. 训练随机森林与基础重要性分析
3.1 模型训练与Gini重要性
from sklearn.ensemble import RandomForestClassifier rf = RandomForestClassifier(n_estimators=100, random_state=42) rf.fit(X, y) # 获取Gini重要性 importances = rf.feature_importances_将重要性结果与特征名关联:
feature_importance = pd.DataFrame({ 'feature': all_features, 'importance': importances }).sort_values('importance', ascending=False) print(feature_importance.head(10))3.2 可视化重要性排序
使用Seaborn绘制水平条形图:
import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(10, 6)) sns.barplot(data=feature_importance, x='importance', y='feature') plt.title('特征重要性排序(Gini指数)') plt.tight_layout() plt.show()典型输出解读:
- 性别(sex)通常是最重要特征
- 票价(fare)和年龄(age)次之
- 船舱等级(pclass)也有显著影响
4. 更稳健的重要性评估:排列重要性
Gini重要性可能受特征尺度影响,排列重要性(Permutation Importance)通常更可靠:
from sklearn.inspection import permutation_importance result = permutation_importance( rf, X, y, n_repeats=10, random_state=42 ) # 整理结果 perm_importance = pd.DataFrame({ 'feature': all_features, 'importance_mean': result.importances_mean, 'importance_std': result.importances_std }).sort_values('importance_mean', ascending=False)可视化排列重要性:
plt.figure(figsize=(10, 6)) sns.barplot(data=perm_importance, x='importance_mean', y='feature', xerr=perm_importance['importance_std']) plt.title('排列重要性(均值±标准差)') plt.tight_layout() plt.show()5. 高级技巧与实战建议
5.1 处理相关特征
当特征高度相关时,重要性会被分散。可以尝试:
# 计算特征相关性 corr_matrix = pd.DataFrame(X, columns=all_features).corr() # 可视化热力图 plt.figure(figsize=(12, 8)) sns.heatmap(corr_matrix, annot=True, fmt=".2f", cmap='coolwarm') plt.title('特征相关性矩阵') plt.show()5.2 重要性评估的稳定性检查
通过不同随机种子验证结果可靠性:
def check_stability(features, target, n_runs=5): stability_scores = {} for feature in features: scores = [] for seed in range(n_runs): rf = RandomForestClassifier(random_state=seed) rf.fit(X, y) importances = rf.feature_importances_ idx = list(features).index(feature) scores.append(importances[idx]) stability_scores[feature] = np.std(scores) return pd.DataFrame.from_dict(stability_scores, orient='index', columns=['std_dev']) stability_df = check_stability(all_features, y) print(stability_df.sort_values('std_dev'))5.3 实际应用中的决策流程
基于特征重要性的典型工作流:
- 初步筛选:去除重要性接近零的特征
- 二次验证:检查剩余特征的排列重要性
- 稳定性测试:多次运行确认关键特征
- 业务对齐:与领域专家讨论发现是否合理
# 示例:选择重要性高于平均值的特征 threshold = feature_importance['importance'].mean() selected_features = feature_importance[feature_importance['importance'] > threshold]['feature'] print(f"筛选后的特征:{list(selected_features)}")6. 常见问题解决方案
6.1 处理高基数类别特征
对于像"乘客姓名"这样的高基数特征:
# 创建姓氏特征代替全名 df['surname'] = df['name'].str.split(',').str[0] # 统计姓氏出现频率 surname_counts = df['surname'].value_counts() # 将低频姓氏归为"其他" df['surname_group'] = df['surname'].apply( lambda x: x if surname_counts[x] > 3 else 'Other' )6.2 重要性结果与业务逻辑冲突时
当模型认为"船舱位置"比"年龄"更重要时:
- 检查数据泄露:测试集信息是否混入训练集
- 验证特征工程:是否合理处理了缺失值
- 尝试不同的重要性计算方法
- 进行交叉验证确保不是随机波动
6.3 在特征选择中的应用
将重要性分析融入Pipeline:
from sklearn.pipeline import Pipeline from sklearn.feature_selection import SelectFromModel pipe = Pipeline([ ('preprocessor', preprocessor), ('selector', SelectFromModel( RandomForestClassifier(n_estimators=100), threshold='median' )), ('classifier', RandomForestClassifier()) ])