用统计检验来确定“重要特征”

在做机器学习建模的时候,经常遇到一个让人头疼的问题:手上有几十甚至上百个特征,到底哪些该留、哪些该丢?

之前两篇介绍了通过 ++方差阈值++ 和 ++相关性分析++ 来过滤特征,今天这篇介绍一种新的过滤特征的方式,也就是用统计检验来判断一个特征到底跟目标变量有没有关系。

核心思路很简单

说白了,统计检验做特征选择的核心逻辑就一句话:

对每个特征单独做一次假设检验,看看它跟目标变量之间到底是"真有关系"还是"纯靠运气"。

统计检验的思路

  • 原假设 H_0 :该特征与目标变量无关
  • 计算 p 值 :如果 p 值很小( \< \\alpha ,通常 \\alpha=0.05 ),说明"特征与目标无关"这个假设大概率不成立→ 拒绝 H_0 → 保留该特征
  • p 值越大 → 越没有证据表明特征有用 → 剔除

就这么简单。

本质上就是给每个特征打一个"显著性分数",然后卡个阈值做筛选。

实现方式

实现这个基于统计显著性检验的特征选择器 ,采用了三层架构。

  1. _CorrectionStrategy(校正策略):处理多重检验的 p 值校正
  2. _TestAdapter(检验方法适配器):统一不同统计检验的调用接口
  3. StatisticalTestSelector(核心选择器):对外暴露的 API

校正策略

对于校正策略部分,代码中封装了 3 种校正策略:

策略 类名 思路
不校正 _NoCorrection(file:///home/wangyubin/projects/python/python-learn/scikit-learn/特征选择/03/statistical_test_selector.py#L26-L31) 直接用原始 p 值,不做任何处理
Bonferroni _BonferroniCorrection(file:///home/wangyubin/projects/python/python-learn/scikit-learn/特征选择/03/statistical_test_selector.py#L34-L44) 最保守:p值 × 检验次数,严格控制假阳性
FDR (BH) _BenjaminiHochbergCorrection(file:///home/wangyubin/projects/python/python-learn/scikit-learn/特征选择/03/statistical_test_selector.py#L47-L87) 折中方案:控制错误发现率,比 Bonferroni 更宽松

其中:

Bonferroni 校正(最简单):

python 复制代码
adjusted_p = original_p × 检验总次数 m

比如检验 100 个特征,p=0.01 会被校正为 1.0(不再显著)。

Benjamini-Hochberg (FDR)(最常用):

plain 复制代码
步骤:
1. 把 p 值从小到大排序
2. 第 k 个 p 值的临界阈值 = (k / m) × α
3. 找到最大的 k,使得 p_(k) ≤ 阈值
4. 前 k 个特征全部保留
5. 反推校正后的 p 值(保证单调性)

检验方法适配器

其实 sklearn 库中提供了多种统计检验方法,但它们的接口不统一:

  • f_classif , chi2 → 返回(scores, pvalues)
  • mutual_info_classif → 只返回 scores ,没有 p

所以,这里封装了_TestAdapter, 统一了调用接口,所有方法都返回 (scores, pvalues)

方法 适用场景 说明
anova 分类任务 ANOVA F 检验,最常用
f_regression 回归任务 F 回归检验
chi2 分类任务 卡方检验,要求特征非负
mutual_info 分类/回归 互信息,不返回 p 值,用归一化得分构造伪 p 值
auto 自动选择 分类用 anova,回归用 f_regression

互信息的伪 p 值处理

python 复制代码
# 互信息没有 p 值,用得分归一化后反推
scores = mutual_info_func(X, y)
normalized = scores / max(scores)   # 归一化到 [0, 1]
pvals = 1.0 - normalized            # 得分越高 → p 值越小 → 越显著

核心选择器

StatisticalTestSelector(file:///home/wangyubin/projects/python/python-learn/scikit-learn/特征选择/03/statistical_test_selector.py#L175-L369) 是对外暴露的主类,兼容 sklearn 的 fit / transform 接口。

核心流程(fit 方法)

plain 复制代码
输入: 特征矩阵 X, 目标变量 y
  │
  ├─ 1. 输入校验(类型、缺失值、样本数一致性)
  │
  ├─ 2. 选择检验方法 → _build_test_adapter()
  │     根据 test_type 和 task 构建适配器
  │
  ├─ 3. 执行统计检验 → adapter.run()
  │     得到每个特征的 (检验得分, p值)
  │
  ├─ 4. 多重检验校正 → strategy.apply()
  │     根据 correction 参数选择校正策略
  │     得到 (是否保留的掩码, 校正后的p值)
  │
  └─ 5. 存储结果
        _scores: 各特征的检验得分
        _raw_pvals: 原始 p 值
        _pvals: 校正后 p 值
        _selected: 被选中的特征名列表

transform 方法

只做一件事:从 X 中取出 _selected 中的列,返回筛选后的 DataFrame

使用示例:

python 复制代码
selector = StatisticalTestSelector(
    alpha=0.05,          # 显著性水平
    correction="fdr",    # FDR 校正
    test_type="auto",    # 自动选择检验方法
)

# 拟合并转换
X_filtered = selector.fit_transform(X, y, task="classification")

# 查看详细报告
report = selector.get_report()
print(report)
# 输出类似:
#   特征名称   检验得分   原始P值   校正P值   是否选中
#   特征A     15.3     0.0001   0.002     ✓ 保留
#   特征B      2.1     0.15     0.30      ✗ 剔除

代码实现

完整的代码和模拟数据的测试用例(用统计检验筛选「体检指标」中的关键特征,预测糖尿病风险)共享在:

++https://url11.ctfile.com/d/45455611-166997307-d6d609?p=6872++ (访问密码: 6872)

相关文件是:statistical_test_selector.pytest_statistical_test_selector.py

测试结果:

plain 复制代码
数据形状: (300, 115),目标分布: {1: 157, 0: 143}
患病率: 52.3%

【方案A】全部 30 个特征
  5折交叉验证准确率: 0.5467 ± 0.0245

【方案B】统计检验筛选后(保留 4 个特征)
  5折交叉验证准确率: 0.8400 ± 0.0564

==================================================
准确率变化: +0.2933 (提升 0.2933)
特征数量: 115 → 4

==================================================

共检验 115 个特征,保留 4 个,剔除 111 个 (α=0.05, 校正=fdr, 检验=auto)

感兴趣的朋友最好能将代码下载运行试试看,机器学习最好的理解方式就是多尝试。

几点注意事项

几个需要注意的地方:

统计检验是"单变量"方法

这类方法每次只看一个特征和目标变量的关系,不会考虑特征之间的交互作用 。比如特征 A 单独看没用,但 A + B 组合起来可能很有用------这种情况统计检验发现不了。所以它更适合做初步筛选,后面可以再配合其他方法(比如基于树模型的特征重要性)做进一步选择。

先看看数据再跑检验

跑之前最好先做一些基本的探索性分析。比如:

  • 特征里有没有缺失值?大多数检验方法不接受缺失值
  • 卡方检验要求特征非负,如果特征有负数,需要做平移
  • 如果特征的尺度差异很大,建议先做标准化

不要盲目信赖 p 值

p 值小于 0.05 不代表这个特征"非常重要",只是说"有统计证据表明它跟目标有关系"。在大样本情况下,即使非常微弱的关系也可能得到很小的 p 值。所以我一般会结合检验得分(比如 F 值的大小)一起看,而不是只看 p 值。

校正方法的选择很重要

如果不做任何校正,直接用原始 p 值筛选的话,在特征少(比如十几个)的时候问题不大,但特征一旦上到几百几千,假阳性问题就很严重了。

我个人的经验:

  • 特征数 < 20:不做校正也行,但最好心里有数
  • 特征数 20 ~ 几百:用 FDR 校正
  • 特征数上千:用 FDR 校正,并且可以考虑更严格的 alpha(比如 0.01)

总结

最后画个简单的流程图,帮自己理清思路:

plain 复制代码
原始特征集(N 个特征)
        │
        ▼
  对每个特征做统计检验
  (ANOVA F / 卡方 / 互信息 / ...)
        │
        ▼
  得到每个特征的 p 值
        │
        ▼
  多重检验校正
  (Bonferroni / FDR)
        │
        ▼
  校正后 p 值 < α ?
   ┌────┴────┐
   │ 是      │ 否
   ▼         ▼
  保留       剔除
        │
        ▼
  筛选后的特征集(K 个特征,K ≤ N)

统计检验做特征选择,优点很明显:原理清晰、计算快速、结果可解释

它不能解决所有特征选择的问题,但作为建模流程中的第一步筛选,我觉得是非常实用的。

相关推荐
薛少杰2 小时前
对标Reddit,Meta悄然推出“Forum”应用
ai·meta·facebook·reddit·forum
时空节拍AI数字人3 小时前
多模态交互数字人:语音+视觉+触控如何融合
人工智能·microsoft·ai·aigc·交互·语音识别
openYuanrong分布式计算引擎3 小时前
openYuanrong 打造 Agent 时代的企业级分布式底座
人工智能·分布式·ai·serverless
关于不上作者榜就原神启动那件事4 小时前
从 MDC 到 Agent:我手搓的文档路由协议,在 Spring AI Alibaba 里找到了正式实现
java·人工智能·spring·ai·agent
人间凡尔赛5 小时前
Kimi K3 技术拆解:2.8 万亿参数开源模型背后的 KDA 线性注意力与 Stable LatentMoE
ai·大模型·注意力机制·moe·kimi
神奇霸王龙6 小时前
MCP v5 Agent Skills 屠夫榜:5 旗舰子代理
网络·人工智能·ai·aigc·agent·mcp·skills
方渐鸿9 小时前
【2026】Pi Agent一键安装使用
ai
thesky12345618 小时前
27届大模型岗面试准备(十三):长上下文与上下文工程——从位置外推到分块策略的完整考点
人工智能·ai·大模型
Husp070721 小时前
Prompt入门到精通系列(一)
ai·语言模型