表格建模前怎么删除常量列?用 VarianceThreshold 核对保留字段

用 VarianceThreshold 删除训练期常量列并保留字段名,检查新数据的字段变化、全常量失败路径及后续预测契约。

训练表格中的某列如果每行都一样,它在这批数据中没有区分样本的变化。可以用 VarianceThreshold(threshold=0.0) 删除这种零方差列,并明确保存保留字段。关键是只在训练集学习删除规则,再把同一规则用于后续数据;“训练期常量”并不证明该字段在未来永远没用。

零方差筛选解决什么问题

本文只处理数值特征中完全不变化的列,不使用标签,不按预测效果给字段排名。它不同于 SelectKBest 的监督统计筛选,也不会处理重复样本、填补缺失值或把字符串类别自动转换成数值。官方特征选择指南说明默认阈值删除所有零方差特征;VarianceThreshold API定义 variances_、get_support()、get_feature_names_out(),并说明没有任何列满足阈值时会抛出 ValueError。

表格建模前怎么删除常量列?用 VarianceThreshold 核对保留字段

以下六条训练记录和两条新记录是人为构造的接口示例。代码已于 2026 年 10 月 1 日在 Windows、Python 3.11.15、scikit-learn 1.7.2、pandas 2.3.3、NumPy 2.4.6、SciPy 1.17.1 运行;没有训练业务模型,也未实测字段删除后对业务效果的影响。

python -m venv .venv
.venv\Scripts\python -m pip install "scikit-learn==1.7.2" "pandas==2.3.3" "numpy==2.4.6" "scipy==1.17.1"

删除常量列,同时把输出列名留下

将下面代码保存为 remove_constants.py,运行 .venv\Scripts\python remove_constants.py。四个字段中 source_code 全是 7,rare_switch 在训练期全是 0;sample_count 和 ratio 有变化。

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

X_train = pd.DataFrame({
    "sample_count": [2, 3, 5, 7, 9, 12],
    "ratio": [0.1, 0.2, 0.3, 0.2, 0.4, 0.5],
    "source_code": [7, 7, 7, 7, 7, 7],
    "rare_switch": [0, 0, 0, 0, 0, 0]
})
X_new = pd.DataFrame({
    "sample_count": [4, 8],
    "ratio": [0.25, 0.35],
    "source_code": [7, 7],
    "rare_switch": [0, 1]
})
selector = VarianceThreshold(threshold=0.0)
selector.fit(X_train)
kept = selector.get_feature_names_out()
removed = X_train.columns[~selector.get_support()].tolist()
train_result = pd.DataFrame(selector.transform(X_train), columns=kept,
                            index=X_train.index)
new_result = pd.DataFrame(selector.transform(X_new), columns=kept,
                          index=X_new.index)
print("训练列方差:", dict(zip(X_train.columns,
                         np.round(selector.variances_, 6).tolist())))
print("保留:", kept.tolist(), "删除:", removed)
print("训练形状:", X_train.shape, "变换后:", train_result.shape)
print("新样本变换:")
print(new_result.to_string(index=False))
assert kept.tolist() == ["sample_count", "ratio"]
assert new_result.shape == (2, 2)
assert train_result.index.equals(X_train.index)
changed_removed = [name for name in removed
                   if not X_new[name].isin(X_train[name].unique()).all()]
print("新数据出现训练期未见取值的已删除列:", changed_removed)
try:
    VarianceThreshold().fit(pd.DataFrame({"constant": [1, 1, 1]}))
except ValueError:
    print("全列常量:已按预期拒绝")
else:
    raise AssertionError("全常量输入应当被拒绝")

selector.fit(X_train) 只执行一次。新样本用 transform(X_new),不重新 fit_transform;这样下游模型始终接受同一组字段。构造输出 DataFrame 时使用 get_feature_names_out() 和原行索引,避免变成没有列名的数组后把字段顺序接错。

读取结果,而不是只看代码没有报错

本次实际输出:

训练列方差: {'sample_count': 10.0, 'ratio': 0.018056, 'source_code': 0.0, 'rare_switch': 0.0}
保留: ['sample_count', 'ratio'] 删除: ['source_code', 'rare_switch']
训练形状: (6, 4) 变换后: (6, 2)
新样本变换:
 sample_count  ratio
          4.0   0.25
          8.0   0.35
新数据出现训练期未见取值的已删除列: ['rare_switch']
全列常量:已按预期拒绝

source_code 和 rare_switch 的训练方差都为 0,因此被删除;保留 sample_count、ratio,训练输入从 6×4 变为 6×2,新数据输出为 2×2。断言核对字段名、输出形状及原行索引。末尾还用全常量输入检查失败路径,出现“全列常量:已按预期拒绝”。

注意 rare_switch 在新数据第二条中变为 1。它仍会被旧规则删除,因为模型的字段契约没有重新训练;代码单独报告该列出现训练期未见取值。真实项目应查这是否属于新场景、上游字段变化或抽样遗漏,再决定补充训练样本并更新整条模型流程。不要临时把它插回已训练模型的输入。

接入自己的 CSV 时保留哪些记录

  1. 先排除目标标签、样本编号和不应参与预测的字段,并记录数值特征名与顺序。
  2. 划分训练与验证,使用训练特征 fit 筛选器;只用验证集 transform。
  3. 记录训练字段、保留字段、删除字段和 variances_,方便解释模型实际收到什么。
  4. 推理时检查缺列、额外列、列顺序和数据类型,再使用原筛选器;多列缺失时停止,不能靠补零悄悄改变业务含义。
  5. 新数据被删列出现新取值时,记录待复核事件;重训时一起更新筛选器和模型,不单改列清单。

阈值、缺失值与失败情况

先用 threshold=0.0 处理“完全常量”。非零阈值会受量纲影响:厘米和米的方差数值不同,不能把同一阈值随意用于所有字段。小方差也不等于无用信号,尤其是罕见但关键的业务开关。本教程没有为你的数据选择非零阈值。

API 允许输入包含 NaN,但这不意味着它完成了缺失值处理。下游分类器是否接受缺失值要另行检查;缺失严重或全空的字段应先按业务规则审查。若所有列都被删除,先排查输入取错、字段变为常数、样本量太小或阈值不合适,不应捕获异常后继续训练空特征。

下一步是用你的训练数据产生一份“字段、训练方差、是否保留”的对照表,人工确认删除项,再在独立验证样本上评估完整模型。删除常量列是输入整理动作,不能代替标签质量、数据泄漏和预测效果检查。

Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30815.html

赞 (0)
AI小管家的头像AI小管家
一条样本要预测多个标签怎么办?用 MultiOutputClassifier 训练并核对输出
上一篇 10小时前
多个分类模型怎么一起投票?用 VotingClassifier 核对硬投票与软投票
下一篇 10小时前

相关推荐

联系我们

联系我们

1

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
关注微信
分享本页
返回顶部