4. 特征工程基础:特征选择、提取与构造

大家好,我是你们的老朋友。今天咱们聊聊特征工程。说实话,很多刚入行的朋友觉得算法模型才是核心,特征工程不过是“洗数据”的杂活。我当年也这么想,直到有一次做PM2.5预测,模型死活调不好,后来发现是特征里混了一堆无关变量。嗯,从那以后,我再也不敢小看特征工程了。

特征工程说白了,就是帮你把原始数据变成模型能“吃”懂的语言。今天咱们重点讲三个方向:特征选择、特征提取、特征构造。我习惯把它们比作“挑菜”、“切菜”和“配菜”。

核心观点:好的特征工程,能让普通模型跑出惊艳的效果。差的特征工程,再牛的算法也白搭。

特征工程三大核心 特征选择 特征提取 特征构造 过滤法(方差、卡方、互信息) 包裹法(RFE、递归特征消除) PCA降维(主成分分析) LDA、t-SNE(可选) 时间滞后特征(Lag Features) 滑动窗口统计量(均值、方差) 目标:减少冗余 → 提升性能 → 增强可解释性

4.1 特征选择:过滤法

过滤法,我理解就是“先筛一遍”。它不依赖任何模型,纯粹从数据本身出发。你想想看,如果某个特征的值几乎不变,它对预测能有多大贡献?

常用的过滤方法:

  • 方差过滤:去掉方差接近0的特征。比如某个传感器读数一直是0.5±0.01,那基本没用。
  • 卡方检验:用于分类问题,看特征和目标变量是否独立。
  • 互信息:衡量特征与目标之间的依赖程度,数值越大越重要。

我的经验:我在做水质预测时,一开始用了30多个特征。用互信息一筛,发现真正有用的只有8个。模型训练时间直接缩短了60%,精度反而提升了。这就是“少即是多”的典型例子。

# 过滤法示例:方差过滤 + 互信息
from sklearn.feature_selection import VarianceThreshold, SelectKBest, mutual_info_regression
import pandas as pd

# 假设 df 是你的环境参数数据框
X = df.drop('target', axis=1)
y = df['target']

# 第一步:去掉低方差特征
selector_var = VarianceThreshold(threshold=0.01)
X_var = selector_var.fit_transform(X)

# 第二步:用互信息选Top-K特征
selector_mi = SelectKBest(score_func=mutual_info_regression, k=10)
X_selected = selector_mi.fit_transform(X_var, y)

# 看看选了哪些特征
selected_mask = selector_mi.get_support()
selected_features = X.columns[selected_mask]
print(f"筛选后的特征: {list(selected_features)}")

4.2 特征选择:包裹法

包裹法比过滤法“聪明”一点,它会用模型来评估特征组合的好坏。说白了,就是让模型自己“试吃”,觉得哪个特征组合好吃就留下。

我个人比较常用的是递归特征消除(RFE)。它的逻辑很简单:先拿所有特征训练模型,然后根据特征重要性,每次去掉最不重要的那个,反复迭代直到达到指定数量。

注意:包裹法计算量比较大。如果你有100个特征,RFE可能要跑100轮模型训练。我曾经在一个项目里等了一个多小时才跑完,后来改用过滤法先粗筛一遍,再用包裹法精调,效率高多了。

# 包裹法示例:递归特征消除
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestRegressor

# 用随机森林作为评估模型
estimator = RandomForestRegressor(n_estimators=100, random_state=42)
selector_rfe = RFE(estimator, n_features_to_select=8, step=1)
selector_rfe.fit(X, y)

# 输出排名
feature_ranking = pd.DataFrame({
    'feature': X.columns,
    'rank': selector_rfe.ranking_
}).sort_values('rank')

print(feature_ranking)

4.3 特征提取:PCA降维

特征提取和特征选择不一样。选择是“挑”,提取是“造”。PCA(主成分分析)就是典型的提取方法,它把原始特征通过线性变换,变成一组新的、互不相关的特征。

你可能会问:为什么要这么做?我举个例子。假设你有温度、湿度、风速三个特征,它们之间可能有很强的相关性。PCA可以把它们压缩成两个“综合指标”,既保留了大部分信息,又去掉了冗余。

关键点:PCA后的新特征没有物理意义,它只是数学上的组合。所以如果你需要解释模型(比如环保报告),PCA可能不太合适。但如果你只关心预测精度,PCA是个好工具。

# PCA降维示例
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

# 标准化(PCA对尺度敏感)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 降维到5个主成分
pca = PCA(n_components=5)
X_pca = pca.fit_transform(X_scaled)

# 看看每个主成分的解释方差比
print(f"各主成分解释方差比: {pca.explained_variance_ratio_}")
print(f"累计解释方差比: {pca.explained_variance_ratio_.cumsum()}")

4.4 特征构造:时间滞后特征

环境数据通常有时间序列特性。今天的PM2.5浓度,和昨天、前天的浓度肯定有关系。时间滞后特征就是把这个关系显式地表达出来。

我记得有一次做空气质量预测,只用了当前时刻的特征,模型R²只有0.6。后来加了前3小时的滞后特征,R²直接跳到0.85。你想想看,这差距有多大。

# 构造时间滞后特征
def create_lag_features(df, target_col, lags=[1, 2, 3, 6, 12, 24]):
    df_lag = df.copy()
    for lag in lags:
        df_lag[f'{target_col}_lag_{lag}h'] = df_lag[target_col].shift(lag)
    # 去掉前几行NaN
    df_lag = df_lag.dropna()
    return df_lag

# 示例:对PM2.5构造滞后特征
df_with_lag = create_lag_features(df, 'pm25', lags=[1, 3, 6, 12])
print(df_with_lag.head())

4.5 特征构造:滑动窗口统计量

滑动窗口统计量,说白了就是看“过去一段时间内”的统计特征。比如过去24小时的平均温度、过去1小时的湿度波动幅度等。这些特征能捕捉到数据的趋势和波动性。

我常用的滑动窗口统计量包括:

  • 均值:反映趋势
  • 标准差:反映波动
  • 最大值/最小值:反映极端情况
  • 偏度/峰度:反映分布形态(这个用得少,但有时很管用)

避坑指南:我曾经在构造滑动窗口特征时,不小心用了未来数据(窗口包含当前时刻之后的值)。结果模型在测试集上表现完美,但上线后一塌糊涂。记住:滑动窗口只能看过去,不能看未来!

# 滑动窗口统计量示例
def create_rolling_features(df, target_col, windows=[3, 6, 12, 24]):
    df_roll = df.copy()
    for w in windows:
        df_roll[f'{target_col}_roll_mean_{w}h'] = df_roll[target_col].rolling(window=w).mean()
        df_roll[f'{target_col}_roll_std_{w}h'] = df_roll[target_col].rolling(window=w).std()
        df_roll[f'{target_col}_roll_max_{w}h'] = df_roll[target_col].rolling(window=w).max()
        df_roll[f'{target_col}_roll_min_{w}h'] = df_roll[target_col].rolling(window=w).min()
    # 去掉NaN
    df_roll = df_roll.dropna()
    return df_roll

# 示例:对温度构造滑动窗口特征
df_with_roll = create_rolling_features(df, 'temperature', windows=[6, 12, 24])
print(df_with_roll.columns.tolist())

4.6 实战建议:如何组合使用

在实际项目中,我一般按这个流程走:

  1. 先构造:根据领域知识,构造滞后特征和滑动窗口特征。
  2. 再过滤:用方差过滤和互信息,快速去掉无用特征。
  3. 后精调:如果特征还是太多,用RFE或PCA进一步压缩。

这个流程我用了很多年,基本没出过大问题。当然,具体项目要具体分析。比如做洪水预测,滞后特征可能更重要;做噪声监测,滑动窗口统计量可能更关键。

最后说一句:特征工程没有银弹。多试、多对比、多思考,才是王道。我见过太多人一上来就调模型参数,结果特征都没处理好。记住:垃圾进,垃圾出。好的特征,是模型成功的一半。


专注资料整理