3. 单变量特征选择(方差过滤):方差阈值法

方差阈值法(Variance Threshold)是单变量特征筛选中最基础、最快速的方法。它不依赖目标变量,仅通过特征自身的离散程度来判断是否保留,属于典型的无监督过滤式特征选择。

3.1 核心原理

方差衡量一组数据的离散程度。对于特征 \(X\),其方差计算公式为:

\(\text{Var}(X) = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})^2\)

方差越小,说明该特征的所有样本取值越接近常数;方差为零时,特征取值完全一致,对模型无任何区分能力。方差阈值法通过设定一个最小方差阈值,自动剔除方差低于该阈值的特征。

关键认知: 方差过滤只关心特征自身的波动大小,不关心特征与标签的相关性。一个方差很大的特征可能是噪声,也可能是有用信号;方差很小的特征几乎一定是冗余或无效的。

3.2 适用场景与局限性

维度 说明
适用场景
  • 特征维度极高(万级以上),需要快速粗筛
  • 存在大量稀疏二值特征(如独热编码、词袋模型)
  • 数据预处理流水线中的第一步过滤
  • 特征取值类型统一(如全部为数值型)
局限性
  • 无法识别与标签相关的低方差特征(如稀有但重要的指标)
  • 对特征尺度敏感,必须先标准化或归一化
  • 不适用于多类别特征(如枚举值)
  • 阈值设定缺乏理论指导,通常依赖经验或网格搜索
⚠ 常见陷阱: 对二值特征(0/1)直接计算原始方差时,方差 = p(1-p),其中 p 为 1 的比例。当 p 接近 0 或 1 时方差很小,但这类特征可能对模型有决定性作用。建议对二值特征单独设置较低的阈值,或结合频次过滤。

3.3 方差阈值法的决策流程

下图展示了方差阈值法在特征筛选流水线中的标准执行步骤:

方差阈值法执行流程 原始特征矩阵 (n×m) 标准化 / 归一化 计算各特征方差 方差 ≥ 阈值? 是 否 保留特征 剔除特征 输出:保留特征子集 → 进入后续建模流程

3.4 Python 实现:VarianceThreshold

scikit-learn 提供了 VarianceThreshold 类,一行代码即可完成方差过滤。以下为完整实现示例:

import numpy as np
import pandas as pd
from sklearn.feature_selection import VarianceThreshold
from sklearn.preprocessing import StandardScaler

# 1. 构造示例数据(包含低方差特征)
np.random.seed(42)
n_samples = 1000

data = pd.DataFrame({
    'feature_1': np.random.randn(n_samples),           # 正常方差 ≈ 1.0
    'feature_2': np.random.randn(n_samples) * 0.1,     # 低方差 ≈ 0.01
    'feature_3': np.random.randn(n_samples) * 0.01,    # 极低方差 ≈ 0.0001
    'feature_4': np.random.randint(0, 2, n_samples),   # 二值特征,方差 ≈ 0.25
    'feature_5': np.ones(n_samples) * 42,              # 零方差(常数)
})

print("原始特征方差:")
print(data.var().round(6))

# 2. 标准化(重要!消除量纲影响)
scaler = StandardScaler()
data_scaled = pd.DataFrame(
    scaler.fit_transform(data),
    columns=data.columns
)

# 3. 方差过滤:保留方差 > 0.1 的特征
selector = VarianceThreshold(threshold=0.1)
data_selected = selector.fit_transform(data_scaled)

# 4. 查看结果
selected_mask = selector.get_support()
selected_features = data.columns[selected_mask]
removed_features = data.columns[~selected_mask]

print(f"\n保留特征 ({len(selected_features)} 个): {list(selected_features)}")
print(f"剔除特征 ({len(removed_features)} 个): {list(removed_features)}")

# 5. 输出过滤后的数据形状
print(f"\n过滤前形状: {data_scaled.shape}")
print(f"过滤后形状: {data_selected.shape}")

3.5 阈值设定策略

阈值的选择直接影响过滤效果。下表总结了不同场景下的推荐做法:

数据类型 推荐阈值 说明
标准化后的连续特征 0.01 ~ 0.1 标准化后方差为 1,低于 0.01 的特征几乎无波动
二值特征 (0/1) 0.8 × p(1-p) 或 0.01 保留至少 5% 样本出现 1 的特征(p > 0.05)
归一化到 [0,1] 的特征 0.005 ~ 0.05 归一化后方差上限为 0.25,阈值不宜过高
高维稀疏数据 0.001 ~ 0.01 稀疏矩阵方差普遍偏小,需降低阈值
💡 实用技巧: 如果不确定阈值,可以先计算所有特征的方差分布,取方差排序后第 5~10 百分位的值作为阈值。或者使用 selector.fit_transform(data) 默认阈值 0,仅剔除零方差特征,再结合业务判断。

3.6 方差过滤与后续特征选择的衔接

方差阈值法通常作为特征筛选的第一道关卡,其输出结果会传递给更精细的选择方法。下图展示了在完整特征工程流水线中的位置:

方差过滤在特征工程流水线中的位置 原始特征 (10000 维) 方差过滤 (剔除 3000 维) 相关性过滤 (剔除 2000 维) 统计检验 (保留 3000 维) 流水线效果示例 原始 10000 维 → 方差过滤后 7000 维 → 相关性过滤后 5000 维 → 统计检验后 3000 维 → 进入模型训练 方差过滤作为第一步,快速降低维度,为后续精细选择减轻计算压力

3.7 注意事项与最佳实践

  • 先标准化,后过滤: 方差对量纲敏感,不同尺度的特征方差不可比。务必先进行 Z-score 标准化或 Min-Max 归一化。
  • 分类型特征单独处理: 对于类别型特征,先进行独热编码,再对每个二值列进行方差过滤。可考虑结合频次过滤(剔除出现次数过少的类别)。
  • 不要过度过滤: 方差阈值过高可能导致丢失有用特征。建议从低阈值开始,逐步调高并观察模型性能变化。
  • 与业务规则结合: 某些低方差特征在业务上具有特殊意义(如“是否死亡”),即使方差很小也应保留。
  • 管道化集成: 使用 sklearn.pipeline.Pipeline 将 VarianceThreshold 与后续模型串联,避免数据泄露。
# 管道化示例:方差过滤 + 逻辑回归
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression

pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('var_filter', VarianceThreshold(threshold=0.05)),
    ('classifier', LogisticRegression())
])

pipeline.fit(X_train, y_train)
print(f"过滤后特征数: {pipeline.named_steps['var_filter'].n_features_in_}")
print(f"模型准确率: {pipeline.score(X_test, y_test):.4f}")