一、为什么数值特征工程最重要?

在机器学习中,“垃圾进,垃圾出” 是铁律。
数值特征(Age、Fare、SibSp 等连续数字)占了数据 70% 以上,但原始数据往往有缺失值、异常值、尺度不一、非线性等问题。

处理好数值特征,模型效果能直接提升 10-50%
(Kaggle Titanic 比赛中,很多选手就是靠数值特征处理从 0.78 冲到 0.85+)

我从零基础开始,用 Titanic 数据集 一步步实践,记录了完整笔记。现在把全流程 + 6个独立Demo + 最终Pipeline + Markdown笔记 整理成这篇博客,供大家直接复制使用。


二、 六大核心技巧一览表(最重要!)


三、6大核心技巧详解(每个都有完整Demo)

1. 标准化(StandardScaler)

该类是什么特征:
数值特征(连续型数字,如年龄、票价)
做什么:
把所有数值转换成均值=0、标准差=1的分布,让不同尺度特征在同一水平线上
为什么选这个特征:
Age(0-80)和Fare(0-65)尺度完全不同,标准化后模型(线性回归、神经网络、KNN)不会被大数字“欺负”,效果提升明显

# -*- coding: utf-8 -*-
# demo_01_StandardScaler.py
import pandas as pd
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv')
features = ['Age', 'Fare']
df[features] = df[features].fillna(df[features].median())

scaler = StandardScaler()
df[['Age_std', 'Fare_std']] = scaler.fit_transform(df[features])

print(df[['Age_std', 'Fare_std']].describe().round(4))

在这里插入图片描述

2. 归一化(MinMaxScaler)

该类是什么特征:
数值特征(连续型数字)
做什么:
– 把数值压缩到0~1之间(最小值→0,最大值→1)
为什么选这个特征:
– 神经网络、决策树、距离类算法(如KNN)对0-1范围更敏感,避免某个特征“一家独大”

import pandas as pd
# MinMaxScaler:  归一化器。将所有数值压缩到 $[0, 1]$ 之间。
from sklearn.preprocessing import MinMaxScaler

df = pd.read_csv("../../data/titanic.csv")
print("=== 归一化(MinMaxScaler)Demo ===")

features = ['Age', 'Fare']
df[features] = df[features].fillna(df[features].median())  # 补全缺失值

# 归一化(核心)
scaler = MinMaxScaler()
# fit : 学习阶段。计算数据的最大值(Max)和最小值(Min)。
# transform: 执行阶段。根据算好的 Max/Min 进行数学转换。
# fit_transform	 组合拳。一次性完成学习和转换。
df[['Age_mm', 'Fare_mm']] = scaler.fit_transform(df[features])

print("归一化后(0~1范围):")
print(df[['Age_mm', 'Fare_mm']].head())
print("\n最小值和最大值:")
print(df[['Age_mm', 'Fare_mm']].describe().loc[['min', 'max']].round(4))

在这里插入图片描述

3.离散化(分箱 / KBinsDiscretizer)

该类是什么特征:
# 数值特征(连续型 → 类别型)
做什么:
– # 把连续数字切成固定几个“箱子”(如0-4),把Age变成“青年/中年/老年”标签
为什么选这个特征:
– 模型更容易学习非线性关系,减少极端值影响,防止过拟合(Kaggle上常用)

核心代码

from sklearn.preprocessing import KBinsDiscretizer
binner = KBinsDiscretizer(n_bins=5, encode='ordinal', strategy='quantile')
df[['Age_bin', 'Fare_bin']] = binner.fit_transform(df[features]).astype(int)

在这里插入图片描述

4. 缺失值填充(SimpleImputer)

该类是什么特征:
# 数值特征(有NaN)
做什么:
– # 用中位数/均值/常数把NaN填满(Age有177个缺失)
为什么选这个特征:
– 模型无法处理NaN,必须先填;中位数最稳健(不受极端值影响)

Demo:

# -*- coding: utf-8 -*-

import pandas as pd
from sklearn.impute import SimpleImputer

df = pd.read_csv("../../data/titanic.csv")
print("=== 缺失值填充Demo ===")

print("填充前Age缺失值数量:", df['Age'].isnull().sum())

# 中位数填充(最推荐)
imputer = SimpleImputer(strategy='median')
df['Age_filled'] = imputer.fit_transform(df[['Age']])

print("填充后缺失值数量:", df['Age_filled'].isnull().sum())
print("填充后Age统计:")
print(df['Age_filled'].describe().round(2)))

在这里插入图片描述

5. 异常值处理(IQR Capping)

该类是什么特征:
# 数值特征(有极端值)
做什么:
– # 把Fare里512元这种超级异常值“压”到合理上限(不删除数据)
为什么选这个特征:
– 极端值会把模型拉歪(尤其是线性模型),capping是最温和有效的处理方式

Demo

df = pd.read_csv("../../data/titanic.csv")
print("=== 异常值处理(IQR Capping)Demo ===")

# 计算IQR
Q1 = df['Fare'].quantile(0.25)
Q3 = df['Fare'].quantile(0.75)
IQR = Q3 - Q1
upper_limit = Q3 + 1.5 * IQR

print(f"Fare异常值上限:{upper_limit:.2f}")

# capping(超过上限的全部设为上限)
df['Fare_capped'] = df['Fare'].clip(upper=upper_limit)

在这里插入图片描述

6. 特征交叉(Cross Feature)

该类是什么特征:
# 数值特征衍生(交叉组合)
做什么:
– # 用Age×Fare、Age/Fare创建新列,捕捉“年龄×财富”这种非线性关系
为什么选这个特征:
– 极端值会把模型拉歪(尤其是线性模型),capping是最温和有效的处理方式
Demo原始特征往往不够强,交叉后模型效果经常提升10-30%(Kaggle神器)

# 先填充缺失值
df['Age'] = df['Age'].fillna(df['Age'].median())

# 创建交叉特征
df['Age_Fare_product'] = df['Age'] * df['Fare']          # 乘法
df['Age_Fare_ratio']   = df['Age'] / (df['Fare'] + 1)    # 除法(+1防除0)
df['Age_bin_Fare']     = df['Age'] // 10 * df['Fare']    # 分箱后交叉

在这里插入图片描述


四、最佳处理顺序(必须背下来!)

1. 缺失值填充
2. 异常值处理
3. 特征交叉
4. 分箱
5. 标准化/归一化

五、工业级 Pipeline(一键处理所有数值特征)

# 01_numerical_pipeline_final.py(已保存 joblib)
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, KBinsDiscretizer, FunctionTransformer

num_pipeline = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),
    ('cap_outlier', FunctionTransformer(cap_outliers)),
    ('binning', KBinsDiscretizer(n_bins=5, encode='ordinal')),
    ('scaler', StandardScaler())
])

X_train_transformed = num_pipeline.fit_transform(X_train)
joblib.dump(num_pipeline, 'numerical_pipeline.joblib')

六、知识点总结表格

序号 技巧名称 作用(做什么) 为什么重要(核心价值) 最佳使用场景 核心函数 / 方法 注意事项 / 常见坑
1 标准化
(StandardScaler)
把数据转换成均值=0、标准差=1 不同尺度特征(Age 0-80 vs Fare 0-512)会让模型“偏心”,标准化后所有特征同等重要 线性模型、神经网络、KNN、SVM StandardScaler() 必须在训练集 fit,测试集只 transform
2 归一化
(MinMaxScaler)
把数据压缩到 0~1 区间 神经网络、距离类算法对 0-1 范围最敏感,避免某个特征“一家独大” 神经网络、决策树、KNN MinMaxScaler() 对异常值敏感,先处理异常值再归一化
3 离散化(分箱)
(Binning)
把连续数字切成固定类别(0、1、2、3、4) 把非线性关系变成“类别”,减少过拟合,模型更容易学习 树模型(随机森林、XGBoost)、有极端值时 KBinsDiscretizer(n_bins=5) 等频(quantile)比等宽好;箱数 5-10 最佳
4 缺失值填充 用中位数/均值/常数填补 NaN 模型无法处理 NaN,必须先填;中位数最稳健(不受极端值影响) 任何有缺失值的数值列 SimpleImputer(strategy='median') 优先用中位数;不要用均值(易被拉偏)
5 异常值处理
(Capping)
把极端值“压”到合理上限(IQR法) 极端值(如 Fare=512)会严重拉歪模型分布,导致预测不准 有明显长尾分布的特征(Fare最典型) clip(upper=upper_limit) 或 IQR 推荐 capping 而不是删除数据
6 特征交叉 创建新特征:x*yx/yx//10 * y 捕捉原始特征无法表达的非线性交互关系(年龄×财富) 几乎所有项目(Kaggle提分神器) 手动计算或 PolynomialFeatures 先填充缺失值再交叉;防止除0加+1

七、常见坑 & 避坑指南

  • ❌ 测试集用了 fit_transform → 数据泄漏!
  • ❌ 用均值填充 Age → 必须用中位数
  • ❌ 分箱后没 .astype(int) → 类型错误
  • ✅ 所有步骤放 Pipeline 中自动处理

从“只会Python + pandas”到今天跑通6个Demo + Pipeline,我用了不到一周。
特征工程真的比模型重要!

欢迎点赞、收藏、评论,你的一个赞就是我继续更新类别特征的动力!

代码仓库https://github.com/codeInbpm/ML-Learning-Journey-2025
完整系列:搜索 “ML-Learning-Journey-2025”

下一篇文章预告类别特征工程全攻略(含WOE编码实战)


你已经迈出了特征工程最重要的一步!🚀

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐