摘要

数据预处理是机器学习项目成功的基石,通常占据数据科学家80%的工作时间。本文将系统性地总结数据预处理的完整流程、关键技术方法和最佳实践,并附带Python代码示例,帮助读者构建全面的数据处理知识体系。

一、数据预处理的重要性与整体流程

1.1 为什么数据预处理如此关键?

  • 数据质量决定模型上限:高质量的数据比复杂的算法更能提升模型性能
  • 现实数据的不完美性:现实世界数据通常存在缺失、噪声、不一致等问题
  • 算法要求:大多数机器学习算法对数据格式和分布有特定要求

1.2 数据预处理完整流程

数据收集 → 数据清洗 → 特征工程 → 数据变换 → 数据集划分 → 特征缩放 → 最终数据集

二、数据清洗:处理不完整、不一致的数据

2.1 缺失值处理

import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer, KNNImputer

# 创建示例数据
data = pd.DataFrame({
    'A': [1, 2, np.nan, 4, 5],
    'B': [np.nan, 2, 3, np.nan, 5],
    'C': [1, 2, 3, 4, 5]
})

# 1. 删除缺失值(仅适用于缺失很少的情况)
data_dropna = data.dropna()
![请添加图片描述](https://i-blog.csdnimg.cn/direct/4d42759b48af4130a4cd13cacc786922.png)

# 2. 均值/中位数/众数填充
imputer_mean = SimpleImputer(strategy='mean')  # 'median','most_frequent'
data_mean = pd.DataFrame(imputer_mean.fit_transform(data), columns=data.columns).round(2) # 四舍五入到两位小数

# 3. 前后值填充(时间序列常用)
data_ffill = data.ffill()  # 前向填充
data_bfill = data.bfill()  # 后向填充

# 4. KNN填充(考虑特征相似性)
knn_imputer = KNNImputer(n_neighbors=2)
data_knn = pd.DataFrame(knn_imputer.fit_transform(data), columns=data.columns)

# 5. 模型预测填充(使用其他特征预测缺失值)
from sklearn.ensemble import RandomForestRegressor
# 此处省略具体实现,思路:用非缺失数据训练模型预测缺失值

请添加图片描述

2.2 异常值检测与处理

import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats

# 创建包含异常值的数据
np.random.seed(42)
data_normal = np.random.normal(0, 1, 1000)
data_with_outliers = np.append(data_normal, [10, -10, 15, -15])

# 1. 基于统计方法的检测
# Z-score方法
z_scores = np.abs(stats.zscore(data_with_outliers))
outliers_z = np.where(z_scores > 3)[0]  # 阈值通常设为3

# IQR方法
Q1 = np.percentile(data_with_outliers, 25)
Q3 = np.percentile(data_with_outliers, 75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = np.where((data_with_outliers < lower_bound) | 
                       (data_with_outliers > upper_bound))[0]

# 2. 可视化检测
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
sns.boxplot(y=data_with_outliers)
plt.title('Boxplot')

plt.subplot(1, 3, 2)
sns.histplot(data_with_outliers, kde=True)
plt.title('Histogram')

plt.subplot(1, 3, 3)
stats.probplot(data_with_outliers, dist="norm", plot=plt)
plt.title('Q-Q Plot')
plt.tight_layout()
plt.show()

# 3. 异常值处理方法
# 删除异常值
data_cleaned = data_with_outliers[(data_with_outliers >= lower_bound) & 
                                 (data_with_outliers <= upper_bound)]

# 缩尾处理(Winsorization)
from scipy.stats.mstats import winsorize
data_winsorized = winsorize(data_with_outliers, limits=[0.05, 0.05])

# 替换为边界值
data_capped = np.copy(data_with_outliers)
data_capped[data_capped < lower_bound] = lower_bound
data_capped[data_capped > upper_bound] = upper_bound

在这里插入图片描述

2.3 重复值处理

# 检测重复行
duplicates = data.duplicated()
num_duplicates = duplicates.sum()

# 删除重复行
data_unique = data.drop_duplicates()

# 基于关键列删除重复
data_unique_subset = data.drop_duplicates(subset=['key_column'])

三、特征工程:从原始数据中提取价值

3.1 特征编码:将类别变量转换为数值

from sklearn.preprocessing import LabelEncoder, OneHotEncoder, OrdinalEncoder
from category_encoders import TargetEncoder, CountEncoder
import pandas as pd

# 示例数据
cat_data = pd.DataFrame({
    'color': ['红', '蓝', '绿', '蓝', '红', '绿'],
    'size': ['大', '中', '小', '大', '中', '小'],
    'target': [1, 0, 1, 0, 1, 0]
})

# 1. 标签编码(适用于有序分类)
label_encoder = LabelEncoder()
cat_data['color_label'] = label_encoder.fit_transform(cat_data['color'])

# 2. 独热编码(适用于无序分类,类别较少)
onehot_encoder = OneHotEncoder(sparse_output=False, drop='first')  # 使用drop避免虚拟变量陷阱
onehot_encoded = onehot_encoder.fit_transform(cat_data[['color']])
onehot_df = pd.DataFrame(onehot_encoded, 
                        columns=onehot_encoder.get_feature_names_out(['color']))

# 3. 目标编码(考虑目标变量的均值)
target_encoder = TargetEncoder()
cat_data['color_target'] = target_encoder.fit_transform(cat_data['color'], 
                                                       cat_data['target'])

# 4. 频率编码(使用类别出现频率)
freq_encoding = cat_data['color'].value_counts(normalize=True)
cat_data['color_freq'] = cat_data['color'].map(freq_encoding)

3.2 特征创建与变换

# 1. 多项式特征(捕获特征交互)
from sklearn.preprocessing import PolynomialFeatures
X = np.array([[1, 2], [3, 4], [5, 6]])
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X)

# 2. 对数/指数变换(处理偏态分布)
data = pd.DataFrame({'income': [20000, 50000, 80000, 150000, 300000]})
data['income_log'] = np.log1p(data['income'])  # log(1+x)避免0值
data['income_sqrt'] = np.sqrt(data['income'])
data['income_boxcox'], _ = stats.boxcox(data['income'] + 1)  # Box-Cox变换

# 3. 分箱/离散化(将连续变量转换为类别)
data['income_bin'] = pd.cut(data['income'], 
                           bins=[0, 50000, 100000, np.inf],
                           labels=['低', '中', '高'])

# 等频分箱
data['income_qbin'] = pd.qcut(data['income'], q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])

# 4. 日期时间特征提取
dates = pd.date_range('2023-01-01', periods=10, freq='D')
date_df = pd.DataFrame({'date': dates})
date_df['year'] = date_df['date'].dt.year
date_df['month'] = date_df['date'].dt.month
date_df['day'] = date_df['date'].dt.day
date_df['dayofweek'] = date_df['date'].dt.dayofweek
date_df['is_weekend'] = date_df['dayofweek'].isin([5, 6]).astype(int)
date_df['quarter'] = date_df['date'].dt.quarter

四、总结与展望

数据预处理是机器学习项目成功的决定性环节,它不仅仅是技术操作,更是一种数据思维的体现。通过本文的系统性梳理,我们可以得出以下关键结论:

  • 质量优于算法:高质量的数据往往比复杂的算法更能提升模型性能。投入时间在数据预处理上,通常能获得比调整模型参数更高的回报率。
  • 流程决定效率:建立标准化的数据预处理流程(数据清洗 → 特征工程 → 数据变换 → 数据集划分)能显著提高工作效率,确保实验的可复现性。
  • 业务驱动技术:每个预处理决策(如缺失值填充策略、特征编码方法)都应基于对业务逻辑的理解,而不仅仅是技术考量。

更多推荐