数据清洗是数据预处理的核心步骤,旨在修正或移除数据集中的错误、不完整、重复或不一致的部分,为后续分析和建模提供可靠基础。以下是数据清洗的详细流程、方法和实战示例:
问题类型 | 表现示例 | 影响 |
---|---|---|
缺失值 | 数值型字段为空(NaN) | 模型无法处理缺失值,导致训练中断或偏差 |
异常值 | 年龄=200岁,房价=-100万 | 扭曲统计指标(如均值),降低模型泛化性 |
重复数据 | 两行记录完全相同 | 导致模型过拟合,降低数据代表性 |
不一致数据 | 日期格式混乱(2023-09-01 vs 01/09/23) | 解析错误,特征提取失败 |
检测缺失值:
# 统计每列缺失比例
missing_ratio = data.isnull().mean() * 100
print(missing_ratio.sort_values(ascending=False))
处理方法:
方法 | 适用场景 | 代码示例 |
---|---|---|
直接删除 | 缺失比例高(>80%)或无关字段 | data.dropna(axis=1, thresh=len(data)*0.2) |
均值/中位数填充 | 数值型字段,缺失随机分布 | data['age'].fillna(data['age'].median(), inplace=True) |
众数填充 | 类别型字段 | data['gender'].fillna(data['gender'].mode()[0], inplace=True) |
插值法 | 时间序列数据(如温度记录) | data['temperature'].interpolate(method='time', inplace=True) |
模型预测填充 | 复杂场景(如多变量关联缺失) | 使用KNN或随机森林预测缺失值(见下方代码) |
KNN填充示例:
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=5)
data_filled = pd.DataFrame(imputer.fit_transform(data), columns=data.columns)
检测方法:
print(data.describe())
plt.figure(figsize=(8,4))
sns.boxplot(x=data['income'])
plt.title("Income Distribution")
plt.show()
z_scores = (data['value'] - data['value'].mean()) / data['value'].std()
outliers = data[abs(z_scores) > 3] # Z>3为异常
Q1 = data['age'].quantile(0.25)
Q3 = data['age'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
处理方法:
方法 | 代码示例 |
---|---|
删除异常值 | data = data[(data['age'] >= 0) & (data['age'] <= 100)] |
截断(Winsorize) | from scipy.stats.mstats import winsorize |
分箱(Binning) | data['age_bin'] = pd.cut(data['age'], bins=[0,18,35,60,100]) |
检测与删除:
# 检测完全重复的行
duplicates = data.duplicated()
print(f"重复行数量: {duplicates.sum()}")
# 删除重复行(保留第一个出现值)
data.drop_duplicates(keep='first', inplace=True)
部分重复处理(如用户ID重复但信息不同):
# 按关键字段去重(如用户ID)
data.drop_duplicates(subset=['user_id'], keep='last', inplace=True)
格式统一:
# 日期格式标准化
data['date'] = pd.to_datetime(data['date'], format='mixed')
# 文本大小写统一
data['category'] = data['category'].str.lower()
# 单位统一(如货币转换)
data['price'] = data['price'].apply(
lambda x: x * 6.5 if 'USD' in x else x
)
逻辑校验:
# 检查年龄与出生日期是否一致
current_year = pd.Timestamp.now().year
data['calculated_age'] = current_year - data['birth_year']
invalid_age = data[abs(data['age'] - data['calculated_age']) > 1]
import pandas as pd
data = pd.DataFrame({
'order_id': [101, 102, 103, 104, 105, 106],
'user_id': [1, 2, 2, 3, 4, None],
'price': [29.9, 199.0, 199.0, -50.0, 89.9, 120.0],
'order_date': ['2023-09-01', '01/09/2023', '2023-09-01', '2023-10-32', None, '2023-09-05']
})
处理缺失值:
# 填充user_id缺失值(假设新用户ID为999)
data['user_id'].fillna(999, inplace=True)
# 删除order_date缺失的行
data.dropna(subset=['order_date'], inplace=True)
修正异常价格:
# 删除价格为负的订单
data = data[data['price'] > 0]
# 截断价格超过200的订单(假设业务上限为200)
data['price'] = data['price'].clip(upper=200)
标准化日期格式:
# 转换日期并过滤无效日期(如2023-10-32)
data['order_date'] = pd.to_datetime(data['order_date'], errors='coerce')
data.dropna(subset=['order_date'], inplace=True)
去重:
# 按user_id和order_date去重(保留最后一条)
data.drop_duplicates(subset=['user_id', 'order_date'], keep='last', inplace=True)
order_id | user_id | price | order_date |
---|---|---|---|
101 | 1 | 29.9 | 2023-09-01 |
102 | 2 | 199.0 | 2023-09-01 |
105 | 4 | 89.9 | NaT(已删除) |
106 | 999 | 120.0 | 2023-09-05 |
from sklearn.pipeline import Pipeline
clean_pipeline = Pipeline([
('fill_na', SimpleImputer(strategy='constant', fill_value=999)),
('remove_duplicates', DropDuplicates(subset=['user_id'])),
('clip_outliers', ColumnTransformer([('clip', FunctionTransformer(lambda x: x.clip(0, 200)), ['price'])])),
])
以下是对数据变换的更紧凑、更细节化的总结,突出核心要点与实用技巧:
方法 | 公式 | 适用场景 | 异常值敏感度 | Scikit-learn工具 |
---|---|---|---|---|
Z-score | z = x − μ σ z = \frac{x - \mu}{\sigma} z=σx−μ | 数据近似正态分布,线性模型(SVM、回归) | 高 | StandardScaler |
Min-Max | x ′ = x − x min x max − x min x' = \frac{x - x_{\min}}{x_{\max} - x_{\min}} x′=xmax−xminx−xmin | 图像像素、神经网络输入层 | 高 | MinMaxScaler |
Robust | x ′ = x − median I Q R x' = \frac{x - \text{median}}{IQR} x′=IQRx−median | 存在异常值,非正态分布 | 低 | RobustScaler |
关键技巧:
np.log1p
避免零值。scipy.stats.boxcox
)。QuantileTransformer
)。示例代码:
from sklearn.preprocessing import PowerTransformer
pt = PowerTransformer(method='yeo-johnson') # 兼容零/负值
X_transformed = pt.fit_transform(X)
方法 | 优点 | 缺点 | 适用模型 |
---|---|---|---|
One-Hot | 无顺序假设,兼容所有模型 | 高维稀疏,需处理共线性 | 线性模型、神经网络 |
Target编码 | 保留类别与目标的关系 | 需防过拟合(如交叉验证) | 树模型、高基数类别 |
Embedding | 低维稠密,捕捉语义相似性 | 需预训练或端到端学习 | 深度学习(NLP/推荐系统) |
关键点:
Target Encoding
或CatBoost
内置处理。Label Encoding
,但需验证类别顺序是否合理。pd.qcut
)或等宽分箱(pd.cut
),捕捉非线性。TF-IDF
,长文本用BERT
嵌入,高维稀疏时用TruncatedSVD
降维。代码示例:
# 时间特征处理
df['hour'] = df['timestamp'].dt.hour
df['is_weekend'] = df['timestamp'].dt.weekday >= 5
方法 | 核心思想 | 适用场景 | 注意事项 |
---|---|---|---|
PCA | 线性投影最大化方差 | 高维数据可视化/去噪 | 需先标准化,可能丢失非线性信息 |
t-SNE | 非线性保留局部结构 | 可视化高维聚类 | 计算代价高,不用于特征输入 |
UMAP | 平衡速度与局部/全局结构 | 大规模数据可视化/预处理 | 参数敏感,需调参 |
经验:
n_components=0.95
)。Pipeline
确保流程:from sklearn.pipeline import make_pipeline
pipe = make_pipeline(StandardScaler(), SVM())
pipe.fit(X_train, y_train)
np.clip
)或中位数填充,避免破坏分布。总结:数据变换需与模型特性深度耦合,通过实验迭代优化。记住:“Garbage in, garbage out”——宁可花80%时间在数据准备,而非调参!
特征工程是机器学习的“炼金术”,旨在将原始数据转化为模型可理解的强特征,直接影响模型性能上限。以下是结构化拆解:
时间特征
df['purchase_hour'] = df['timestamp'].dt.hour
df['days_since_last_purchase'] = (current_date - df['last_purchase_date']).dt.days
交互特征(组合特征)
df['price_per_sqmeter'] = df['total_price'] / df['area']
统计聚合特征
user_stats = orders.groupby('user_id')['amount'].agg(['mean', 'max'])
文本/图像特征
分箱(Binning)
df['age_bin'] = pd.cut(df['age'], bins=[0,18,35,60,100], labels=['child', 'young', 'adult', 'senior'])
非线性变换
np.log1p
避免零值)高基数类别处理
方法 | 原理 | 适用场景 |
---|---|---|
过滤法 | 基于统计指标(如方差、卡方检验) | 快速初筛,计算成本低 |
包裹法 | 通过模型性能迭代选择特征子集 | 精确但计算代价高(递归特征消除) |
嵌入法 | 模型训练中自动选择(如L1正则化) | 与模型耦合,高效 |
实用技巧:
feature_importances_
筛选重要性>阈值特征# FeatureTools示例
import featuretools as ft
es = ft.EntitySet()
es = es.entity_from_dataframe(entity_id='users', dataframe=users_df, index='user_id')
features, feature_defs = ft.dfs(entityset=es, target_entity='users')
避免数据泄露:
Pipeline
封装预处理与模型训练:from sklearn.pipeline import Pipeline
pipe = Pipeline([('encoder', TargetEncoder()), ('model', RandomForest())])
领域知识驱动:
迭代验证:
总结:特征工程是融合领域知识、数据直觉与工程技巧的艺术。掌握核心方法后,需在业务场景中反复迭代,才能炼出“模型友好”的金牌特征。