1. 核心比喻:厨房烹饪流水线
想象你是一个厨师,要做一道菜(比如炒菜)。原始食材(数据)不能直接下锅,需要预处理:
生蔬菜(原始数据) → 清洗(StandardScaler) → 切块(OneHotEncoder) → 调味(添加特征) → 成品(可用于炒菜的食材)
在机器学习中,这个预处理流程就是 transform!
2. 最简单的 transform 例子:标准化
场景:把考试成绩从“0-100分”变成“平均0,标准差1”的标准分
import numpy as np
from sklearn.preprocessing import StandardScaler
# 原始成绩(4个学生,2门课)
# 中文和数学成绩,每门课满分不同
raw_scores = np.array([
[85, 150], # 学生1:语文85,数学150
[60, 120], # 学生2:语文60,数学120
[95, 180], # 学生3:语文95,数学180
[70, 135] # 学生4:语文70,数学135
])
print("原始成绩:")
print(raw_scores)
print(f"语文平均分: {raw_scores[:, 0].mean():.1f}, 数学平均分: {raw_scores[:, 1].mean():.1f}")
输出:
原始成绩:
[[ 85 150]
[ 60 120]
[ 95 180]
[ 70 135]]
语文平均分: 77.5, 数学平均分: 146.2
问题:两门课分数范围不一样,比较不公平!
解决方案:标准化(StandardScaler)
# 创建转换器(就像请一个专门的标准化工序师傅)
scaler = StandardScaler()
# 步骤1:先学习(fit)- 师傅看数据,记住平均值和标准差
scaler.fit(raw_scores) # 师傅说:"我知道了,语文平均77.5,标准差…"
# 步骤2:再转换(transform)- 师傅开始工作
standard_scores = scaler.transform(raw_scores)
print("\\n标准化后的成绩:")
print(standard_scores)
print(f"\\n现在:每门课平均分 = {standard_scores.mean():.3f}")
print(f"每门课标准差 = {standard_scores.std():.3f}")
输出:
标准化后的成绩:
[[ 0.47243196 0.22601794]
[-1.08761082 -1.14981194]
[ 1.08761082 1.50758041]
[-0.47243196 -0.58378641]]
现在:每门课平均分 = 0.000
每门课标准差 = 1.000
3. transform 的两步结构:先学再做
关键理解:
fit:只看数据,学习规则(不改变数据)
transform:应用规则,转换数据
fit_transform:两步合并,一步完成
4. 常用 transform 示例大全
示例1:MinMaxScaler – 缩放到固定范围
from sklearn.preprocessing import MinMaxScaler
# 把成绩缩放到 0-1 范围(常用于神经网络)
scores = np.array([85, 60, 95, 70]).reshape(-1, 1) # 需要二维
scaler = MinMaxScaler()
scaled = scaler.fit_transform(scores)
print("原始成绩:", scores.flatten())
print("缩放后(0-1):", scaled.flatten())
print(f"转换规则: 最小值={scaler.data_min_[0]}, 最大值={scaler.data_max_[0]}")
输出:
原始成绩: [85 60 95 70]
缩放后(0-1): [0.71428571 0. 1. 0.28571429]
转换规则: 最小值=60.0, 最大值=95.0
示例2:OneHotEncoder – 文字变数字
from sklearn.preprocessing import OneHotEncoder
# 学生的年级:初一、初二、初三
grades = np.array([['初一'], ['初三'], ['初二'], ['初一'], ['初三']])
encoder = OneHotEncoder(sparse_output=False)
encoded = encoder.fit_transform(grades)
print("原始年级:")
print(grades.flatten())
print("\\n独热编码后(每个年级一列,1表示是,0表示不是):")
print(encoded)
print(f"\\n编码器记住的类别: {encoder.categories_}")
输出:
text
原始年级:
['初一' '初三' '初二' '初一' '初三']
独热编码后(每个年级一列,1表示是,0表示不是):
[[1. 0. 0.]
[0. 0. 1.]
[0. 1. 0.]
[1. 0. 0.]
[0. 0. 1.]]
编码器记住的类别: [array(['初一', '初二', '初三'], dtype=object)]
示例3:SimpleImputer – 填充缺失值
from sklearn.impute import SimpleImputer
# 有缺失的成绩表(NaN表示缺考)
scores_with_missing = np.array([
[85, 90],
[np.nan, 75], # 语文缺考
[92, np.nan], # 数学缺考
[70, 85]
])
print("原始数据(有缺失):")
print(scores_with_missing)
imputer = SimpleImputer(strategy='mean') # 用平均值填充
filled = imputer.fit_transform(scores_with_missing)
print("\\n填充后(用平均值填充NaN):")
print(filled)
print(f"\\n填充的值: 语文={imputer.statistics_[0]:.1f}, 数学={imputer.statistics_[1]:.1f}")
输出:
原始数据(有缺失):
[[85. 90.]
[nan 75.]
[92. nan]
[70. 85.]]
填充后(用平均值填充NaN):
[[85. 90. ]
[82.33 75. ]
[92. 83.33]
[70. 85. ]]
填充的值: 语文=82.33, 数学=83.33
5. 完整的 transform 流水线(Pipeline)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
# 模拟学生数据
student_data = np.array([
# 语文分, 数学分, 年级
[85, 150, '初一'],
[60, 120, '初三'],
[95, 180, '初二'],
[70, 135, '初一']
])
# 拆分成特征
X_numeric = student_data[:, :2].astype(float) # 前两列是数值
X_categorical = student_data[:, 2].reshape(-1, 1) # 第三列是文字
print("原始数据:")
print(student_data)
# 创建转换管道
numeric_transformer = Pipeline(steps=[
('scaler', StandardScaler()) # 数值标准化
])
categorical_transformer = Pipeline(steps=[
('onehot', OneHotEncoder()) # 文字编码
])
# 组合转换器
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, [0, 1]), # 列0-1用数值转换
('cat', categorical_transformer, [2]) # 列2用分类转换
]
)
# 一步完成:学习规则 + 转换
transformed = preprocessor.fit_transform(student_data)
print("\\n转换后的数据:")
print(transformed)
print(f"\\n形状: {transformed.shape}")
print("说明: 前2列是标准化成绩,后3列是年级的独热编码")
6. transform 在 PyTorch 中的样子
import torch
import torch.nn as nn
# PyTorch 也有 transform,通常通过 nn.Module 实现
class SimpleNormalizer(nn.Module):
"""自定义标准化转换器"""
def __init__(self):
super().__init__()
self.mean = None
self.std = None
def fit(self, X):
"""学习规则"""
self.mean = X.mean(dim=0)
self.std = X.std(dim=0)
return self
def transform(self, X):
"""应用转换"""
return (X – self.mean) / self.std
def forward(self, X):
"""PyTorch风格"""
return self.transform(X)
# 使用示例
X_tensor = torch.tensor([[85.0, 150.0],
[60.0, 120.0],
[95.0, 180.0],
[70.0, 135.0]])
normalizer = SimpleNormalizer()
normalizer.fit(X_tensor)
X_normalized = normalizer.transform(X_tensor)
print("PyTorch转换结果:")
print(X_normalized)
print(f"平均值: {X_normalized.mean(dim=0)}")
print(f"标准差: {X_normalized.std(dim=0)}")
7. 什么时候用 transform?
✅ 必须用 transform 的情况:
不同尺度特征:身高(米) vs 体重(公斤)
文字特征:性别、城市、颜色
缺失值:有缺考、缺勤记录
异常值:特别高或特别低的分数
非线性关系:需要多项式特征
❌ 不需要 transform 的情况:
所有特征已经是 0-1 范围
树模型(随机森林、决策树)通常不需要标准化
只有一两个简单特征
8. 常见错误和正确用法
❌ 错误用法:
# 错误1:用训练集规则转换测试集
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.fit_transform(X_test) # 错误!应该用transform,不是fit_transform
# 错误2:在拆分数据前转换
X_scaled = scaler.fit_transform(all_data) # 泄露了测试集信息!
✅ 正确用法:
# 正确流程
scaler = StandardScaler()
# 只在训练集上学习规则
X_train_scaled = scaler.fit_transform(X_train)
# 用同样的规则转换测试集
X_test_scaled = scaler.transform(X_test) # 注意:只有transform!
📌 一句话总结:
transform = 厨房预处理 fit = 师傅看菜谱学习怎么做 transform = 师傅动手处理食材 fit_transform = 师傅边学边做(但只用于训练数据)
记住:测试集只用 transform,不用 fit,否则就是"作弊"!


