1.数据清洗与预处理——Python数据挖掘(数据抽样、数据分割、异常值处理、缺失值处理)
在数据挖掘与机器学习项目中,数据清洗与预处理是确保模型效果的关键步骤。本文将以实践为导向,介绍数据抽样、数据分割、异常值处理以及缺失值处理的原理、策略和实现代码,帮助您构建一个完善的数据预处理流程。
文章目录
- 一、 数据抽样
- 1.1 抽样方法简介
- 示例代码:抽样演示
- 二、 数据分割
- 2.1 训练集、验证集与测试集
- 2.2 分割方法与注意事项
- 示例代码:数据分割
- 三、 异常值处理
- 3.1 异常值检测
- 示例代码:异常值检测与处理
- 四、 缺失值处理
- 4.1 缺失值检测与统计
- 示例代码:检测缺失值
- 4.2 缺失值填充策略
- 示例代码 1:使用 Pandas 的 fillna() 填充缺失值
- 示例代码 2:使用 scikit-learn 的 SimpleImputer 进行填充
- 总结
一、 数据抽样
1.1 抽样方法简介
- 随机抽样:直接从数据中随机选择一定比例的样本,适用于数据总体分布均匀的情况。
- 分层抽样:根据某个或多个特征先将数据划分为不同“层”,再在各层中随机抽样,这样可以确保各类别(或层)的比例在样本中得到充分体现,避免抽样偏差。
提示: 抽样偏差指的是样本未能真实反映总体情况,从而导致后续模型效果降低。保证抽样的代表性是提高模型泛化能力的重要前提。
示例代码:抽样演示
import pandas as pd
import numpy as np
# 构造一个示例 DataFrame,其中 'target' 表示类别
data = {
'feature1': np.arange(1, 21),
'feature2': np.random.randint(1, 100, 20),
'target': [0]*10 + [1]*10 # 假设前10个样本属于类别0,后10个样本属于类别1
}
df = pd.DataFrame(data)
print("原始数据:")
print(df.head())
# 随机抽样:从 DataFrame 中随机抽取 50% 的样本
random_sample = df.sample(frac=0.5, random_state=42)
print("\n随机抽样结果:")
print(random_sample)
# 分层抽样:确保类别0和类别1的比例一致
# 使用 Pandas 的 groupby 与 sample 进行简单的分层抽样
stratified_sample = df.groupby('target', group_keys=False).apply(lambda x: x.sample(frac=0.5, random_state=42))
print("\n分层抽样结果:")
print(stratified_sample)
二、 数据分割
为了构建并评估模型,我们通常将数据集划分为训练集、验证集和测试集。
2.1 训练集、验证集与测试集
- 训练集:用于模型参数的学习。
- 验证集:用于调参和模型选择,防止模型过拟合。
- 测试集:用于评估模型在未知数据上的最终表现。
2.2 分割方法与注意事项
常用的方法有“留出法”和“交叉验证”。本文采用留出法,并使用 scikit-learn 中的 train_test_split
函数实现,同时通过 stratify
参数实现分层分割,保证各类别比例一致。
示例代码:数据分割
from sklearn.model_selection import train_test_split
# 以前面构造的 DataFrame df 为例,其中 'target' 为分层依据
# 首先将数据划分为训练集(60%)和临时集(40%)
train_df, temp_df = train_test_split(df, test_size=0.4, random_state=42, stratify=df['target'])
# 再将临时集均分为验证集和测试集(各占20%)
val_df, test_df = train_test_split(temp_df, test_size=0.5, random_state=42, stratify=temp_df['target'])
print("\n训练集样本数:", len(train_df))
print("验证集样本数:", len(val_df))
print("测试集样本数:", len(test_df))
print("\n训练集类别分布:")
print(train_df['target'].value_counts())
print("验证集类别分布:")
print(val_df['target'].value_counts())
print("测试集类别分布:")
print(test_df['target'].value_counts())
三、 异常值处理
异常值是指那些与大多数数据差异较大的值,可能由数据采集错误或真实的极端情况引起。常用的检测方法有 Z-score 和 IQR(四分位距)方法。此处我们采用 IQR 方法进行演示。
3.1 异常值检测
- IQR 方法:
- 计算数据的第一四分位数(Q1)和第三四分位数(Q3)。
- 计算 IQR = Q3 - Q1。
- 定义异常值边界:低于 Q1 - 1.5IQR 或高于 Q3 + 1.5IQR 的数据被视为异常值。
示例代码:异常值检测与处理
# 构造一个包含异常值的示例数据集
df_outlier = pd.DataFrame({
'feature': [10, 12, 12, 13, 12, 14, 100] # 此处 100 为明显异常值
})
print("\n原始数据:")
print(df_outlier)
# 计算 Q1、Q3 与 IQR
Q1 = df_outlier['feature'].quantile(0.25)
Q3 = df_outlier['feature'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 标记异常值
df_outlier['is_outlier'] = ((df_outlier['feature'] < lower_bound) | (df_outlier['feature'] > upper_bound))
print("\n异常值检测结果:")
print(df_outlier)
# 处理策略:删除异常值
df_clean = df_outlier[~df_outlier['is_outlier']].drop(columns=['is_outlier'])
print("\n删除异常值后的数据:")
print(df_clean)
补充说明:
除了删除异常值外,另一种方法是对异常值进行修正或用临近值插补,具体策略需根据业务需求决定。
四、 缺失值处理
缺失值问题在实际数据中非常常见。缺失值处理主要包括检测、统计和填充。合理的填充方法可以避免因数据不完整导致的信息损失和偏差。
4.1 缺失值检测与统计
使用 Pandas 的 isnull()
、notnull()
和 info()
方法,可以快速识别数据中的缺失值情况。
示例代码:检测缺失值
# 构造包含缺失值的示例数据集
df_missing = pd.DataFrame({
'feature1': [1, 2, np.nan, 4, 5],
'feature2': [np.nan, 3, 6, 2, 7],
'target': [0, 1, 0, 1, 0]
})
print("\n数据概览:")
print(df_missing.info())
print("\n缺失值统计:")
print(df_missing.isnull().sum())
4.2 缺失值填充策略
在数据预处理中,处理缺失值是一个不可忽视的环节。缺失值如果不加处理可能会影响模型的稳定性和预测准确性。常见的填充方法包括:
- 均值填充:适用于数值型数据,当数据分布较为对称时,用均值替换缺失值可以减少偏差。
- 中位数填充:当数据中存在异常值时,中位数比均值更加稳健,可以减少异常值对填充值的影响。
- 众数填充:适用于分类变量,对于文本或离散数据,用出现频率最高的值进行填充。
- 插值法:基于数据趋势进行线性或多项式插值,常用于时间序列数据,可以通过前后趋势预测缺失值。
下面通过具体代码示例来展示这些填充策略。
示例代码 1:使用 Pandas 的 fillna() 填充缺失值
import pandas as pd
import numpy as np
# 构造包含缺失值的示例数据集
df_missing = pd.DataFrame({
'feature1': [1, 2, np.nan, 4, 5],
'feature2': [np.nan, 3, 6, 2, 7],
'category': ['A', 'B', np.nan, 'B', 'A']
})
print("原始数据:")
print(df_missing)
# 策略一:直接删除包含缺失值的记录(谨慎使用,可能导致信息丢失)
df_dropna = df_missing.dropna()
print("\n删除缺失值后的数据:")
print(df_dropna)
# 策略二:使用均值填充(适用于数值型变量,数据分布较为对称时)
df_fill_mean = df_missing.copy()
df_fill_mean['feature1'].fillna(df_fill_mean['feature1'].mean(), inplace=True)
df_fill_mean['feature2'].fillna(df_fill_mean['feature2'].mean(), inplace=True)
print("\n使用均值填充后的数据:")
print(df_fill_mean)
# 策略三:使用中位数填充(适用于受异常值影响的数据)
df_fill_median = df_missing.copy()
df_fill_median['feature1'].fillna(df_fill_median['feature1'].median(), inplace=True)
df_fill_median['feature2'].fillna(df_fill_median['feature2'].median(), inplace=True)
print("\n使用中位数填充后的数据:")
print(df_fill_median)
# 策略四:使用众数填充(适用于分类变量)
df_fill_mode = df_missing.copy()
df_fill_mode['category'].fillna(df_fill_mode['category'].mode()[0], inplace=True)
print("\n使用众数填充后的数据(针对分类变量):")
print(df_fill_mode)
# 策略五:插值法填充(主要用于时间序列数据或具有一定趋势的数据)
# 此处以 feature1 为例进行线性插值
df_interpolate = df_missing.copy()
df_interpolate['feature1'] = df_interpolate['feature1'].interpolate(method='linear')
print("\n使用插值法填充后的数据(线性插值):")
print(df_interpolate)
使用插值法填充后的数据(线性插值):
对 feature1,缺失值(索引2)被前后两个有效值 2 与 4 线性插值填充为 3.0; 对 feature2,索引0处为 NaN,位于边界位置,线性插值默认不外推,因此保持 NaN; category 列未处理,依然保留原样。
示例代码 2:使用 scikit-learn 的 SimpleImputer 进行填充
SimpleImputer
类可以自动应用多种填充策略,并适用于数值型和分类型数据。下面展示如何利用 SimpleImputer 分别采用中位数和众数策略进行填充。
from sklearn.impute import SimpleImputer
# 对数值型特征采用中位数策略进行填充
num_imputer = SimpleImputer(strategy='median')
# 选取数值型列进行填充,这里为 'feature1' 和 'feature2'
df_numeric = df_missing[['feature1', 'feature2']]
df_numeric_imputed = num_imputer.fit_transform(df_numeric)
df_missing[['feature1', 'feature2']] = df_numeric_imputed
print("\n使用 SimpleImputer(中位数)填充后的数值型数据:")
print(df_missing[['feature1', 'feature2']])
# 对分类变量采用众数策略进行填充
cat_imputer = SimpleImputer(strategy='most_frequent')
df_cat = df_missing[['category']]
df_cat_imputed = cat_imputer.fit_transform(df_cat)
df_missing[['category']] = df_cat_imputed
print("\n使用 SimpleImputer(众数)填充后的分类数据:")
print(df_missing[['category']])
补充说明
-
选择填充策略时的注意事项
- 填充前建议对数据进行可视化和描述性统计分析,明确数据的分布特征。
- 对于数值型变量,若数据存在极端值,使用中位数填充通常比均值填充更稳健。
- 分类变量的填充可以通过众数、固定值或引入新的类别(如 “Missing”)实现。 -
工具与方法说明
- Pandas fillna():是 Pandas 内置的缺失值填充函数,适用于快速实现简单填充策略。
- SimpleImputer:来自 scikit-learn 的预处理模块,提供更灵活的填充策略,尤其在构建数据处理流水线时非常有用。
- 插值方法:在时间序列分析中尤为常见,除了线性插值外,还有多项式插值、时间插值等方法可供选择。
总结
本文详细讲解了数据清洗与预处理的核心步骤:
- 数据抽样:通过随机抽样和分层抽样确保样本具有代表性。
- 数据分割:利用训练集、验证集和测试集的划分为后续模型训练、调参与评估提供支持。
- 异常值处理:通过 IQR 方法检测异常值,并提供删除(或替换)的策略。
- 缺失值处理:识别数据中的缺失情况,采用删除或填充(均值、中位数、众数、插值等)方法补全数据。
通过结合 Pandas、NumPy、scikit-learn 以及 SimpleImputer 等工具,您可以在 Jupyter Notebook 中构建出一套完整的数据预处理流程,为后续的数据挖掘和机器学习任务打下坚实基础。希望本文能帮助您深入理解数据清洗与预处理的实践步骤,并在项目中取得良好效果。