第5篇 | 数据预处理与清洗
数据预处理是机器学习项目的第一步,也是至关重要的一步。真实世界的数据往往存在缺失值、异常值、重复记录等问题,直接使用原始数据训练模型会严重影响模型性能。
一、数据质量的重要性
Garbage In, Garbage Out是机器学习领域的经典名言。即使使用最复杂的算法,如果输入数据质量差,结果也不会理想。数据预处理的目标是将原始数据转换为高质量、可用于建模的数据。
二、缺失值处理
缺失值是最常见的数据问题之一。处理方法包括:删除含有缺失值的行或列、用均值/中位数/众数填充、使用模型预测填充等。
代码示例:缺失值处理
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.impute import KNNImputer
from sklearn.preprocessing import StandardScaler
import warnings
warnings.filterwarnings("ignore")
print("=" * 60)
print("第5篇:数据预处理与清洗")
print("=" * 60)
# 创建模拟数据集
np.random.seed(42)
n_samples = 500
data = {
"age": np.random.randint(18, 70, n_samples).astype(float),
"income": np.random.lognormal(10, 1, n_samples),
"experience": np.random.randint(0, 40, n_samples).astype(float),
"education": np.random.choice([9, 12, 14, 16, 18, 22], n_samples).astype(float),
"city": np.random.choice(["Beijing", "Shanghai", "Shenzhen"], n_samples)
}
df = pd.DataFrame(data)
# 添加缺失值(约15%的数据)
mask = np.random.random(n_samples) < 0.15
df.loc[mask, "income"] = np.nan
df.loc[mask, "experience"] = np.nan
# 添加重复行
df = pd.concat([df, df.iloc[:5]], ignore_index=True)
print(f"原始数据集形状: {df.shape}")
print(f"缺失值统计:")
print(df.isnull().sum())
print(f"重复行数: {df.duplicated().sum()}")
# 方法1:删除法
df_deleted = df.dropna()
df_deleted = df_deleted.drop_duplicates()
print(f"删除缺失值后: {df_deleted.shape}")
# 方法2:中位数填充
df_filled = df.copy()
df_filled["income"].fillna(df_filled["income"].median(), inplace=True)
df_filled["experience"].fillna(df_filled["experience"].median(), inplace=True)
print(f"中位数填充后缺失值: {df_filled.isnull().sum().sum()}")
# 方法3:KNN插补
print("使用KNN插补...")
numeric_cols = ["age", "income", "experience", "education"]
knn_imputer = KNNImputer(n_neighbors=5)
df_knn = df.copy()
df_knn[numeric_cols] = knn_imputer.fit_transform(df[numeric_cols])
print(f"KNN插补后缺失值: {df_knn.isnull().sum().sum()}")
三、异常值检测与处理
异常值可能是数据录入错误,也可能是真实的极端情况。
代码示例:异常值检测
print("\n" + "=" * 50)
print("异常值检测方法")
print("=" * 50)
income_data = df["income"].dropna().values
# Z-score方法(3σ原则)
mean = np.mean(income_data)
std = np.std(income_data)
z_scores = np.abs((income_data - mean) / std)
outliers_zscore = income_data[z_scores > 3]
print(f"Z-score方法检测到 {len(outliers_zscore)} 个异常值")
# IQR方法
Q1 = np.percentile(income_data, 25)
Q3 = np.percentile(income_data, 75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = income_data[(income_data < lower_bound) | (income_data > upper_bound)]
print(f"IQR方法检测到 {len(outliers_iqr)} 个异常值")
print(f"IQR边界: [{lower_bound:.2f}, {upper_bound:.2f}]")
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.boxplot(income_data, vert=True)
plt.title("Income Boxplot")
plt.ylabel("Income")
plt.subplot(1, 2, 2)
plt.hist(income_data, bins=50, alpha=0.7, color="steelblue")
plt.axvline(x=mean, color="red", linestyle="--", label=f"Mean: {mean:.0f}")
plt.title("Income Distribution")
plt.legend()
plt.tight_layout()
plt.savefig("outlier_detection.png", dpi=150)
plt.show()
print("\n异常值处理策略:")
print("1. 删除:如果是明显的录入错误")
print("2. 替换:用边界值替换(winsorization)")
print("3. 变换:对数变换可以减小极端值影响")
四、数据标准化与归一化
不同特征的量纲和取值范围差异很大。
代码示例:数据标准化
print("\n" + "=" * 50)
print("数据标准化与归一化")
print("=" * 50)
np.random.seed(42)
X = np.random.randn(1000, 4)
X[:, 0] = X[:, 0] * 0.1
X[:, 1] = X[:, 1] * 50 + 100
X[:, 2] = X[:, 2] * 10 + 5
X[:, 3] = X[:, 3] * 100000
print("原始特征范围:")
print(f" Feature0: [{X[:, 0].min():.3f}, {X[:, 0].max():.3f}]")
print(f" Feature1: [{X[:, 1].min():.1f}, {X[:, 1].max():.1f}]")
print(f" Feature3: [{X[:, 3].min():.0f}, {X[:, 3].max():.0f}]")
# 标准化
scaler = StandardScaler()
X_standard = scaler.fit_transform(X)
print("\n标准化后统计:")
print(f" 均值: {X_standard.mean(axis=0).round(3)}")
print(f" 标准差: {X_standard.std(axis=0).round(3)}")
五、类别编码
处理类别型特征的方法包括标签编码和独热编码。
代码示例:类别特征编码
from sklearn.preprocessing import LabelEncoder
data = {
"city": ["Beijing", "Shanghai", "Shenzhen", "Hangzhou", "Beijing"],
"education": ["Bachelor", "Master", "PhD", "Bachelor", "Master"]
}
df_cat = pd.DataFrame(data)
print("原始数据:")
print(df_cat)
# 标签编码
le = LabelEncoder()
df_labeled = df_cat.copy()
for col in df_cat.columns:
df_labeled[col + "_encoded"] = le.fit_transform(df_cat[col])
print("\n标签编码结果:")
print(df_labeled)
# 独热编码
df_onehot = pd.get_dummies(df_cat, columns=["city"])
print("\n独热编码结果:")
print(df_onehot)
print("\n编码选择:")
print("- 标签编码:适合有序类别")
print("- 独热编码:适合无序类别,类别数不宜过多")
六、总结
- 数据预处理是机器学习的关键步骤
- 缺失值处理:删除、填充(均值/中位数/KNN)
- 异常值检测:Z-score法、IQR法
- 数据标准化:标准化适合大多数场景
- 类别编码:标签编码适合有序,独热编码适合无序
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)