一、缺失值处理:给数据 “补补丁”
现实中的数据往往不完美 —— 传感器故障、人工录入遗漏、系统报错等,都会导致数据缺失。如果直接忽略这些 “窟窿”,模型很可能学到错误的规律。处理缺失值,我们需要分两步走:先检测,再填补。
1. 如何发现缺失值?
在 Python 中,pandas
库的isnull()
函数是检测缺失值的 “利器”。它会逐单元格判断是否为空,返回True
(缺失)或False
(非缺失)。但要注意:数据中可能存在 “伪装” 的缺失值,比如n/a
、na
、-
等字符串,需要先用na_values
参数统一标记为NaN
(pandas 中标准的缺失值符号)。
import pandas as pd
# 定义“伪装”的缺失值
missing_values = ["n/a", "na", "-"]
# 读取数据时自动将这些值转为NaN
df = pd.read_csv('property-data.csv', na_values=missing_values)
# 检测NUM_BEDROOMS列的缺失值
print(df['NUM_BEDROOMS'].isnull())
2. 缺失值怎么处理?
根据数据特点和业务场景,缺失值的处理方式主要有两种:删除和填充。
-
删除法(dropna ()):如果缺失值占比极低,且随机分布,可以直接删除包含缺失值的行或列。
dropna()
的参数很灵活:axis=0
(默认):删除含缺失值的行;axis=1
:删除含缺失值的列。how='any'
(默认):只要有一个缺失值就删除;how='all'
:所有值都缺失才删除。subset=['列名']
:只检查指定列的缺失值。
# 删除所有含缺失值的行 new_df = df.dropna()
-
填充法(fillna ()):大部分情况下,我们更倾向于填充缺失值而非删除,避免丢失有效信息。常用的填充策略有:
- 常数填充:用固定值(如 0、666)填充,适合对缺失含义有明确假设的场景。
df.fillna(666, inplace=True) # 用666填充所有缺失值
- 统计量填充:用均值(
mean()
)、中位数(median()
)或众数(出现次数最多的值)填充,更贴合数据分布。例如,用均值填充街道编号(ST_NUM
)的缺失值:x = df["ST_NUM"].mean() # 计算均值 df["ST_NUM"].fillna(x, inplace=True) # 填充缺失值
- 专业工具填充:
scikit-learn
的SimpleImputer
支持更标准化的填充,尤其适合后续建模流程。比如用中位数填充年龄缺失值:from sklearn.impute import SimpleImputer imp_median = SimpleImputer(strategy="median") # 定义中位数填充器 age_filled = imp_median.fit_transform(age.reshape(-1, 1)) # 填充年龄列
- 常数填充:用固定值(如 0、666)填充,适合对缺失含义有明确假设的场景。
二、数据标准化:让数据 “站在同一起跑线”
想象一下:如果你的数据中,“身高” 以厘米为单位(范围 150-190),“体重” 以千克为单位(范围 50-100),模型可能会错误地认为 “身高” 对结果的影响更大(因为数值范围更广)。这就是量纲不一致带来的问题。标准化的作用,就是通过转换让不同特征 “无量纲化”,消除这种偏差。
1. 标准化的核心目标
将数据转换为均值为 0、方差为 1 的标准正态分布(或固定范围),让不同特征的 “影响力” 趋于均衡。
2. 两种常用的标准化方法
-
最大最小值标准化(MinMaxScaler):将数据压缩到指定范围(默认 [0,1]),公式为: \(x' = \frac{x - \text{min}(x)}{\text{max}(x) - \text{min}(x)}\) 适合需要明确数据范围的场景(如神经网络输入)。例如,将数据映射到 [5,10] 区间:
from sklearn.preprocessing import MinMaxScaler data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]] scaler = MinMaxScaler(feature_range=[5, 10]) # 指定范围 result = scaler.fit_transform(data) # 输出:[[5.,5.], [6.25,6.25], ...]
-
Z 值标准化(StandardScaler):基于均值和标准差转换,公式为: \(x' = \frac{x - \mu}{\sigma}\)(\(\mu\)为均值,\(\sigma\)为标准差) 转换后的数据均值为 0、方差为 1,适合数据近似正态分布的场景。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() x_std = scaler.fit_transform(data) # 标准化数据 print(x_std.mean()) # 输出:0.0(均值为0) print(x_std.std()) # 输出:1.0(方差为1)
三、特征编码:给文字标签 “贴数字标签”
机器学习模型只能处理数值,而现实数据中常有 “性别(男 / 女)”“学历(小学 / 初中 / 高中)” 等分类特征。特征编码的作用,就是将这些文字标签转换为模型可理解的数字。
1. 先分清特征类型
- 名义变量:无顺序关系(如性别、血型)。
- 有序变量:有顺序但不可计算(如学历、评分等级)。
2. 对应编码方法
-
独热编码(OneHotEncoder):适合名义变量。例如,将 “血型(A/B/AB/O)” 转换为 4 个二进制特征: A 型→(1,0,0,0),B 型→(0,1,0,0),以此类推。这样避免了模型误解 “数字大小”(如 A=1、B=2 会被认为 B>A)。
from sklearn.preprocessing import OneHotEncoder encoder = OneHotEncoder() blood_type_encoded = encoder.fit_transform(blood_type.reshape(-1, 1)).toarray()
-
序号编码(OrdinalEncoder):适合有序变量。例如,“学历” 可编码为:小学 = 1、初中 = 2、高中 = 3,保留顺序关系。
from sklearn.preprocessing import OrdinalEncoder encoder = OrdinalEncoder(categories=[['小学', '初中', '高中']]) education_encoded = encoder.fit_transform(education.reshape(-1, 1))
-
目标标签编码(LabelEncoder):专门用于编码 “目标变量”(即预测结果 y),例如将 “ Survived(Yes/No)” 编码为 1/0。
from sklearn.preprocessing import LabelEncoder encoder = LabelEncoder() survived_encoded = encoder.fit_transform(survived) # Yes→1,No→0
四、数据二值化:非黑即白的 “简化术”
有时我们需要将连续数据 “一刀切”:比如将 “年龄 > 30 岁” 定义为 1,“≤30 岁” 定义为 0。这种将数据分为 0 和 1 的操作,就是二值化,常用sklearn.preprocessing.Binarizer
实现。
from sklearn.preprocessing import Binarizer
# 将年龄按阈值30二值化
X = age.values.reshape(-1, 1) # 转换为二维数组
transformer = Binarizer(threshold=30) # 阈值设为30
age_binarized = transformer.fit_transform(X) # 输出:>30→1,≤30→0
二值化适合突出 “是否满足某条件” 的场景,比如 “是否为高收入人群”“是否有逾期记录” 等。
总结:数据预处理的核心原则
数据预处理没有 “万能公式”,但遵循以下原则能让你少走弯路:
- 先理解数据:缺失值是随机还是有规律?特征的物理含义是什么?
- 结合业务场景:填充策略、标准化方法需匹配业务目标(如预测房价和预测疾病的预处理重点不同)。
- 工具选对事半功倍:
pandas
适合数据清洗,scikit-learn
的preprocessing
模块适合标准化建模流程。