机器学习数据预处理
做机器学习项目时:“数据决定了模型的上限,算法只是逼近这个上限”。而数据预处理,就是把原始数据打磨成可用 的建模素材的关键步骤。最近系统梳理了数据预处理的核心流程,从缺失值处理到特征编码
一、缺失值处理:先搞定数据里的 缺失
拿到数据集第一件事,我总会先检查有没有缺失值 —— 毕竟谁也不想用 “半截数据” 训练模型。原始数据里的缺失值藏得很隐蔽,有的是明显的 “NaN”,有的是 “n/a”“na” 这种文字形式,甚至还有用 “-” 代替的,不仔细处理很容易被忽略。
1. 用 Pandas 快速处理:适合小数据探索
如果是用 Pandas 读数据,第一步得先让它 “认识” 所有缺失值。默认情况下 Pandas 只认 “NaN”,遇到 “n/a” 这种特殊标记,得在read_csv时用na_values参数明确告诉它。比如读房产数据时,我会这么写:
之后用isnull()就能快速定位缺失值,它会返回一个布尔值列表,True就代表这里是空的,一眼就能看出哪列缺得比较多。
处理缺失值无非两种思路:删或填。如果缺失值特别少,比如 1000 条数据里只缺了 10 条,直接用dropna()删掉就行。不过这个函数有很多细节,比如how='any'是 “有一个空就删”,how='all'是 “全空才删”,如果只想检查某几列的缺失值,还能用subset=[列名1, 列名2]指定,避免误删有用数据。
但如果缺失值多,删了就太可惜了,这时候就得 “填”。我常用三种填充方式:填固定值、填均值、填中位数。比如给缺失的房屋面积填个默认值 666,就用df.fillna(666, inplace=True);如果想更合理,就用该列的均值或中位数 —— 均值适合数据分布比较均匀的情况,中位数则不怕 outliers(比如某套房面积突然标了 10000 平,均值会被拉偏,但中位数影响不大)。计算均值和中位数也很简单:
2. 用 Scikit-learn 标准化处理:适合建模 pipeline
如果数据要接入机器学习模型,比如用随机森林、线性回归,光用 Pandas 就不够了 —— 因为 Sklearn 的模型不接受 Pandas 的 DataFrame 直接输入,而且需要更标准化的处理流程。这时候SimpleImputer就派上用场了,它是 Sklearn 专门用来处理缺失值的工具,支持四种常用策略:
- 均值填充:适合数值型特征且数据接近正态分布的情况,比如年龄、收入。用法很固定:先初始化填充器,再用
fit_transform拟合数据并填充,注意输入必须是二维数组(所以要加reshape(-1,1))。 - 中位数填充:如果数据里有 outliers,比如某个人的收入填了 1 亿,用中位数就比均值靠谱,只需要把
strategy改成 “median”。 - 常数填充:有时候业务上有特殊需求,比如缺失的 “是否入住” 标记统一填 0,就用
strategy="constant",再指定fill_value=0。 - 众数填充:这个专门给分类特征用,比如 “出发港口”(S、C、Q)这种没有大小关系的类别,众数就是出现次数最多的那个值,把
strategy设为 “most_frequent” 就行。
二、数据标准化:让特征在同一起跑线
处理完缺失值,下一个坑就是 “特征量级不一致”。比如做房价预测,“房屋面积” 的单位是平方米(数值可能是 100、200),“距离市中心距离” 的单位是公里(数值可能是 1、5),这两个特征的量级差了 100 倍。如果直接输入模型,模型会误以为 “面积” 的影响比 “距离” 大 100 倍,导致参数估计跑偏 —— 这就是为什么要做标准化。
简单说,标准化就是 “无量纲化”,把不同规格的数据拉到同一范围,常见的有两种方法:
1. MinMaxScaler:把数据缩到指定范围
这种方法会把数据压缩到一个你指定的区间里,默认是 [0,1],也可以改成 [5,10] 这种自定义范围。原理很简单:用每个数据减去该列的最小值,再除以(最大值 - 最小值),公式大概是(x - min) / (max - min)。
我之前做用户行为分析时,把 “浏览时长”(0-3600 秒)和 “点击次数”(0-100 次)都缩到 [0,1],模型训练时就不会偏科了。如果想改范围,只需要在初始化时加feature_range=[5,10],比如:
2. StandardScaler:把数据变成标准正态分布
另一种更常用的方法是 Z 值标准化,它会把数据处理成 “均值为 0,方差为 1” 的标准正态分布。这种方法更适合数据本身接近正态分布的情况,而且能保留数据原始的分布关系 —— 比如某个特征里的异常值,标准化后依然是异常值,不会被过度压缩。
用法和 MinMaxScaler 差不多,初始化后拟合变换就行。我每次用的时候都会检查一下结果:标准化后的均值应该接近 0,方差接近 1,这样才说明处理对了。比如:
三、特征编码:把 “文字” 翻译成 “数字”
机器学习模型只认数字,不认文字 —— 比如 “性别” 里的 “男”“女”,“学历” 里的 “小学”“初中”,这些分类特征必须转成数字才能用。但不同类型的分类特征,编码方式完全不一样,这一步要是搞错了,模型结果会差很多首先得区分三种特征类型:
- 名义变量:类别之间没有顺序,比如性别(男 / 女)、血型(A/B/AB/O),不能说 “男> 女” 或 “A>B”。
- 有序变量:类别之间有顺序,比如学历(小学 < 初中 < 高中 < 大学)、成绩(低 < 中 < 高)。
- 有距变量:本身就是数字,而且能计算,比如分数(100、90、60),这种其实不用编码,直接用就行。
针对前两种变量,常用三种编码方法:
1. 独热编码(OneHotEncoder):给名义变量用
如果给名义变量用序号编码(比如男 = 1,女 = 2),模型会误以为 “女> 男”,这显然不对。独热编码就能解决这个问题:它会给每个类别创建一个新列,用 1 表示 “属于这个类别”,0 表示 “不属于”。比如血型有 4 个类别,就会变成 4 列,A 型是 [1,0,0,0],B 型是 [0,1,0,0],这样就不会有虚假的大小关系了。
不过独热编码有个缺点:如果类别太多(比如 “职业” 有 20 个类别),会生成很多新列,导致数据维度爆炸,这时候可能需要用其他方法(比如嵌入),但日常处理少量类别的名义变量,独热编码还是最常用的。
2. 序号编码(OrdinalEncoder):给有序变量用
对于有顺序的特征,比如成绩 “低、中、高”,就适合用序号编码 —— 直接给它们编上有顺序的数字。需要注意的是,序号编码的数字只是 “顺序标记”,没有实际意义 —— 比如不能说 “高” 比 “低” 大 2,只是代表顺序而已。Sklearn 的OrdinalEncoder专门做这个,输入字符串数组,输出整数数组,很方便。
3. 目标标签编码(LabelEncoder):给目标值用
这种编码专门处理模型的目标值(也就是 y),比如分类任务里的 “是否患病”(是 / 否)、“用户等级”(青铜 / 白银 / 黄金)。它会把目标标签转成 0 到 n-1 的整数
但要注意:LabelEncoder只能用在 y 上,不能用在输入特征 X 上!比如把 X 里的 “性别” 用它编码成 0 和 1,虽然结果看起来和独热编码一样,但本质上还是序号编码,会让模型误解类别间的关系,这点一定要记牢。
四、数据二值化:把连续数据 “切一刀” 分成两类
有时候我们需要把连续特征变成 “是 / 否” 的二分类特征,比如把 “年龄” 分成 “大于 30 岁” 和 “小于等于 30 岁”,把 “收入” 分成 “高于 5000” 和 “低于 5000”—— 这就是数据二值化,核心是选一个 “阈值”,大于阈值的记为 1,小于等于的记为 0
二值化的好处是能简化模型,比如用逻辑回归做分类时,二值化后的特征更容易解释,但缺点是会丢失连续数据里的细节,所以要不要用,得看具体业务场景。
整理了一份常用工具的功能表:
| 函数 | 功能 | 适用 |
|---|---|---|
SimpleImputer |
缺失值填充(均值 / 中位数 / 众数 / 常数) | 建模时标准化处理缺失值 |
MinMaxScaler |
数据缩放到指定范围(默认 [0,1]) | 需要固定特征范围的场景 |
StandardScaler |
Z 值标准化(均值 0,方差 1) | 数据接近正态分布时 |
OneHotEncoder |
独热编码 | 名义变量(无顺序的分类特征) |
OrdinalEncoder |
序号编码 | 有序变量(有顺序的分类特征) |
LabelEncoder |
目标标签编码 | 处理模型的目标值 y |
Binarizer |
连续数据二值化 | 需将连续特征转为二分类特征 |
每一步都要结合数据特点和业务需求来定。比如缺失值是填均值还是中位数,标准化用 MinMax 还是 Standard,都需要不断尝试和调整。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)