数据预处理方法合集(数据清洗、数据变换与数据编码)
数据预处理是数据科学和机器学习项目成功的基础步骤。通过适当的数据预处理,可以确保数据的质量、提升模型的性能,并为后续的建模和分析打下坚实的基础。忽视数据预处理可能导致模型训练失败或性能不佳,甚至得出错误的结论。因此,数据预处理在数据驱动的项目中是不可或缺的步骤,接下来我们将详细地介绍具体的方法,文末可获得全文PDF!
目录
对数变换(Logarithmic Transformation)
分位数变换(Quantile Transformation)
数据预处理的必要性
1. 显著提高模型的准确性
通过清洗数据,去除噪声、异常值和处理缺失数据,模型可以更准确地捕捉数据中的真实模式,避免被异常或不完整的数据误导。此外,通过特征缩放(如标准化和归一化),可以使特征之间在模型中具有相同的重要性,特别是在使用基于距离的算法时,特征缩放尤为关键。
2. 提高模型的训练速度和效率
通过特征选择和特征提取,减少数据集的维度,可以大幅降低模型的复杂性和计算时间。这不仅节省了计算资源,还能使模型在面对大规模数据集时表现更好。
3. 改善模型的泛化能力
通过消除数据中的偏差和偏态,模型可以更好地适应新数据,从而提高预测的稳定性。特征工程中的特征生成和选择可以挖掘出数据中隐含的重要信息,使模型在不同的场景中具有更强的泛化能力。
4.确保数据的完整性和一致性
处理缺失值、重复数据和不一致的数据格式,可以防止模型因数据质量问题而产生偏差或错误。对于需要满足特定假设的模型(如正态分布假设),数据预处理可以调整数据使其符合这些假设,从而提高模型的可靠性。
5.增强模型的可解释性和鲁棒性
通过特征选择去除无关或冗余特征,模型的结果更加简洁明了,决策过程更易于理解。同时,处理异常值和噪声数据,可以使模型在面对极端情况时更加稳定,不容易受到意外的干扰。
数据清洗
缺失值处理
1) 删除含有缺失值的行或列
删除含有缺失值的数据行或列,适用于当缺失值比例较低或缺失值随机分布时。这样可以避免缺失值对分析或建模的影响。
import pandas as pd
# 创建一个示例DataFrame
data = {'A': [1, 2, None, 4],
'B': [5, None, None, 8],
'C': [9, 10, 11, 12]}
df = pd.DataFrame(data)
# 删除含有缺失值的行
df_cleaned_rows = df.dropna()
print("删除含有缺失值的行:")
print(df_cleaned_rows)
# 删除含有缺失值的列
df_cleaned_cols = df.dropna(axis=1)
print("\n删除含有缺失值的列:")
print(df_cleaned_cols)
2) 用均值/中位数/众数填充缺失值
用样本的均值、中位数或众数填充缺失值,适用于连续变量或分类变量,能够减少数据的偏差。 当缺失值比例适中,且数据接近正态分布时,使用均值或中位数填充可以保持数据分布的特性。
# 用均值填充缺失值
df_filled_mean = df.fillna(df.mean())
print("用均值填充缺失值:")
print(df_filled_mean)
# 用中位数填充缺失值
df_filled_median = df.fillna(df.median())
print("\n用中位数填充缺失值:")
print(df_filled_median)
# 用众数填充缺失值
df_filled_mode = df.fillna(df.mode().iloc[0])
print("\n用众数填充缺失值:")
print(df_filled_mode)
3) 插值法(线性插值、样条插值等)
使用相邻数据点的趋势对缺失值进行插值,适用于时间序列数据或连续数据,能够较好地保持数据的趋势和变化。
# 线性插值填充缺失值
df_interpolated_linear = df.interpolate(method='linear')
print("线性插值填充缺失值:")
print(df_interpolated_linear)
# 样条插值填充缺失值
df_interpolated_spline = df.interpolate(method='spline', order=2)
print("\n样条插值填充缺失值:")
print(df_interpolated_spline)
4) 使用KNN、MICE等算法填补缺失值
使用机器学习算法或统计方法根据其他特征填补缺失值,能够充分利用数据之间的相关性。适用于复杂数据集、特征间存在较强相关性的场景。
-
• KNN:使用K个最近邻的数据的加权平均值或众数填充。
import pandas as pdy
from sklearn.impute import KNNImputer
# 创建一个示例DataFrame
data = {'A': [1, 2, None, 4],
'B': [5, None, None, 8],
'C': [9, 10, 11, 12]}
df = pd.DataFrame(data)
# 使用KNN算法填补缺失值
imputer = KNNImputer(n_neighbors=2) # 设置邻居数为2
df_filled_knn = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
print("使用KNN算法填补缺失值后的数据:")
print(df_filled_knn)
-
• MICE(多重插补):基于多重插补方法,利用其他特征估计缺失值。
import pandas as pd
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
# 创建一个示例DataFrame
data = {'A': [1, 2, None, 4],
'B': [5, None, None, 8],
'C': [9, 10, 11, 12]}
df = pd.DataFrame(data)
# 使用MICE算法填补缺失值
imputer = IterativeImputer(max_iter=10, random_state=0) # 迭代次数设置为10
df_filled_mice = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
print("使用MICE算法填补缺失值后的数据:")
print(df_filled_mice)
异常值处理
1) 使用3σ法则识别和处理异常值
正态分布下的异常值检测:数据在均值𝜇的基础上,超过3σ的范围可视为异常值。异常值范围=[μ−3σ,μ+3σ]
# 创建一个示例DataFrame
data = {'A': [1, 2, 3, 100, 5],
'B': [10, 20, 30, 40, 50]}
df = pd.DataFrame(data)
# 计算均值和标准差
mean = df['A'].mean()
std = df['A'].std()
# 识别异常值
df['is_outlier'] = ((df['A'] < mean - 3*std) | (df['A'] > mean + 3*std))
print("识别出的异常值:")
print(df)
# 删除异常值
df_no_outliers = df[~df['is_outlier']]
print("\n删除异常值后的数据:")
print(df_no_outliers)
2) 箱线图法(四分位距法)处理异常值
基于四分位距识别和处理异常值,适用于数据分布未知或数据具有偏态的情况。
在箱线图中,数据分布通过箱体(box)表示,中间的线代表数据的中位数。箱体的上下边界表示第一四分位数(Q1)和第三四分位数(Q3),即数据的中间50%。图中用虚线标出的上下界外的点被视为异常值。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 生成一个示例数据集,包含一些异常值
np.random.seed(42)
data = np.random.normal(60, 15, 250) # 生成250个均值为60,标准差为15的正态分布数据
data = np.append(data, [15, 16, 75, 100, 120]) # 添加一些异常值
df = pd.DataFrame(data, columns=['Values'])
# 计算四分位数和四分位距
Q1 = df['Values'].quantile(0.25)
Q3 = df['Values'].quantile(0.75)
IQR = Q3 - Q1
# 计算下界和上界
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 识别异常值
df['Outlier'] = ((df['Values'] < lower_bound) | (df['Values'] > upper_bound))
# 可视化箱线图
plt.figure(figsize=(12, 7))
plt.boxplot(df['Values'], vert=False, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='darkblue'),
medianprops=dict(color='red', linewidth=2),
whiskerprops=dict(color='darkblue', linewidth=1.5),
capprops=dict(color='darkblue', linewidth=1.5),
flierprops=dict(marker='o', color='orange', markersize=8))
# 添加上下界和异常值标注
plt.axvline(x=lower_bound, color='purple', linestyle='--', label=f'Lower Bound ({lower_bound:.2f})')
plt.axvline(x=upper_bound, color='purple', linestyle='--', label=f'Upper Bound ({upper_bound:.2f})')
# 标题和标签
plt.title('Enhanced Box Plot of Values with Outlier Detection', fontsize=16)
plt.xlabel('Value', fontsize=14)
plt.legend(fontsize=12)
plt.grid(True, linestyle='--', alpha=0.6)
# 显示图表
plt.show()
重复数据处理
1) 删除重复的行或记录
删除数据集中完全相同的重复行,以确保每条记录都是唯一的,避免数据重复对分析结果的影响。数据采集或合并过程中产生的重复数据需要清除。
# 创建一个示例DataFrame
data = {'A': [1, 2, 2, 4],
'B': [5, 6, 6, 8],
'C': [9, 10, 10, 12]}
df = pd.DataFrame(data)
# 删除重复行
df_unique = df.drop_duplicates()
print("删除重复行后的数据:")
print(df_unique)
2) 聚合重复项
对于重复数据,根据需要对其进行聚合(如求平均值、总和等),适用于需要保留重复信息但要整合分析的场景。
# 创建一个示例DataFrame
data = {'A': [1, 2, 2, 4],
'B': [5, 6, 6, 8],
'C': [9, 10, 10, 12]}
df = pd.DataFrame(data)
# 按列'A'进行分组,并计算列B和C的平均值
df_aggregated = df.groupby('A').mean().reset_index()
print("聚合重复项后的数据:")
print(df_aggregated)
数据变换
标准化(Normalization)
1) Min-Max缩放(归一化到[0,1]范围)
Min-Max缩放将数据缩放到指定的范围内(通常是[0, 1]),使得数据的最小值变为0,最大值变为1。公式如下:x′=max(x)−min(x)x−min(x)适用场景: 当不同特征的量纲不同且不具有正态分布时,适合使用Min-Max缩放。例如,在图像处理和神经网络中常使用这种方法。
from sklearn.preprocessing import MinMaxScaler
import numpy as np
# 生成示例数据
data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# Min-Max缩放到[0, 1]范围
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(data)
print("原始数据:\n", data)
print("Min-Max缩放后的数据:\n", scaled_data)
2) MaxAbs缩放(将数据按绝对值最大值缩放到[-1, 1]范围)
MaxAbs缩放将数据缩放到[-1, 1]范围内,适用于数据中可能包含负值的情况。公式如下:x′=max(∣x∣)x适用场景: 特别适用于稀疏数据(如文本特征矩阵),在保持稀疏性的同时,确保数据在[-1, 1]范围内。
from sklearn.preprocessing import MaxAbsScaler
# 生成示例数据
data = np.array([[1, -2, 3], [4, 5, -6], [-7, 8, 9]])
# MaxAbs缩放到[-1, 1]范围
scaler = MaxAbsScaler()
scaled_data = scaler.fit_transform(data)
print("原始数据:\n", data)
print("MaxAbs缩放后的数据:\n", scaled_data)
归一化(Standardization)
1) Z-score标准化(均值为0,标准差为1)
Z-score标准化将数据转换为均值为0、标准差为1的标准正态分布。公式如下:x′=σx−μ适用场景: 适用于数据接近正态分布的场景,尤其是在算法中对数据分布敏感的模型,如线性回归和支持向量机。
from sklearn.preprocessing import StandardScaler
# 生成示例数据
data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# Z-score标准化
scaler = StandardScaler()
scaled_data = scaler.fit_transform(data)
print("原始数据:\n", data)
print("Z-score标准化后的数据:\n", scaled_data)
2) 均值移除和方差缩放
这种方法是Z-score标准化的一部分,通过均值移除和方差缩放使得数据的均值为0,方差为1。与Z-score标准化相同,适用于对数据分布敏感的算法。
# 均值移除和方差缩放(与Z-score相同)
scaled_data = scaler.fit_transform(data)
print("原始数据:\n", data)
print("均值移除和方差缩放后的数据:\n", scaled_data)
对数变换(Logarithmic Transformation)
1) 自然对数变换以减少偏态数据的影响
自然对数变换用于减少数据的偏态,使数据更接近正态分布。
适用场景: 适用于数据具有强偏态或存在异方差的情况,如收入数据。公式如下:x′=log(x)
import numpy as np
# 生成示例数据
data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 自然对数变换
log_transformed_data = np.log(data + 1) # 为了避免对数零的问题,常常加1
print("原始数据:\n", data)
print("自然对数变换后的数据:\n", log_transformed_data)
2) Log(x+1)变换以处理零值或负值
# Log(x+1)变换
log1p_transformed_data = np.log1p(data) # 直接使用numpy的log1p函数
print("原始数据:\n", data)
print("Log(x+1)变换后的数据:\n", log1p_transformed_data)
Box-Cox变换
Box-Cox变换是一种幂变换方法,用于减少数据的偏态或提高数据的正态性。它适用于正值数据。公式如下:x′=λxλ−1,ifλ=0x′=log(x),ifλ=0适用于正态性假设的统计模型,如线性回归。需要确保数据为正值。
from scipy.stats import boxcox
# 生成示例数据
data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
# Box-Cox变换
boxcox_transformed_data, fitted_lambda = boxcox(data)
print("原始数据:\n", data)
print("Box-Cox变换后的数据:\n", boxcox_transformed_data)
print("拟合的lambda值:\n", fitted_lambda)
Yeo-Johnson变换
Yeo-Johnson变换是Box-Cox变换的推广,适用于包含零值和负值的数据。公式与Box-Cox类似,但不要求数据为正值。
适用场景: 适用于数据可能包含负值的场景,同样适用于正态性假设的统计模型。
-
• 对于X>0时X′(λ)={λ(X+1)λ−1log(X+1)ifλ=0ifλ=0
-
• 对于X<0时X′(λ)={2−λ−(∣X∣+1)2−λ−1−log(∣X∣+1)ifλ=2ifλ=2
from sklearn.preprocessing import PowerTransformer
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 创建包含负值和正值的示例数据
data = np.array([[-10], [-5], [0], [5], [10], [15], [20]])
# 使用Yeo-Johnson变换
pt = PowerTransformer(method='yeo-johnson')
yj_data = pt.fit_transform(data)
print("Yeo-Johnson变换后的数据:")
print(yj_data)
# 可视化原始数据和变换后的数据
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
sns.histplot(data, kde=True, bins=10, color='blue')
plt.title("Original Data Distribution")
plt.subplot(1, 2, 2)
sns.histplot(yj_data, kde=True, bins=10, color='green')
plt.title("After Yeo-Johnson Transformation")
plt.tight_layout()
plt.show()
分位数变换(Quantile Transformation)
分位数变换通过将数据的分位数映射到目标分布上(通常是均匀分布或正态分布),使得原始数据被调整为所需的分布形式。它适用于对数据进行非线性变换,以减少偏态或改变分布特性。
from sklearn.preprocessing import QuantileTransformer
# 创建示例数据
data = np.array([[1], [2], [2], [3], [3], [3], [4], [5], [5], [6]])
# 应用分位数变换,将数据调整为均匀分布
qt = QuantileTransformer(output_distribution='uniform')
qt_uniform_data = qt.fit_transform(data)
print("分位数变换后的数据(均匀分布):")
print(qt_uniform_data)
# 应用分位数变换,将数据调整为正态分布
qt = QuantileTransformer(output_distribution='normal')
qt_normal_data = qt.fit_transform(data)
print("\n分位数变换后的数据(正态分布):")
print(qt_normal_data)
数据编码
标签编码(Label Encoding)
标签编码是一种简单的编码方法,它将类别变量的每个类别映射为一个唯一的整数。例如,'Red', 'Green', 'Blue' 可以分别映射为 0, 1, 2。
适用场景: 适用于类别之间有序的情况,如“低、中、高”这种有明确顺序的类别。标签编码保留了类别之间的顺序关系。
from sklearn.preprocessing import LabelEncoder
# 创建示例数据
data = ['Red', 'Green', 'Blue', 'Green', 'Red', 'Blue']
# 实例化LabelEncoder并进行编码
encoder = LabelEncoder()
encoded_data = encoder.fit_transform(data)
print("标签编码后的数据:")
print(encoded_data)
print("类别对应关系:", dict(zip(encoder.classes_, range(len(encoder.classes_)))))
独热编码(One-Hot Encoding)
独热编码是一种将类别变量转换为二进制向量的方法。每个类别都被转换为一个二进制向量,其中只有一个位置为1,其余为0。例如,'Red', 'Green', 'Blue' 可以分别编码为 [1, 0, 0], [0, 1, 0], [0, 0, 1]。
适用场景: 适用于无序类别,避免引入顺序关系。独热编码特别适合处理没有固有顺序的分类变量。注意: 独热编码可能导致维度爆炸,特别是当类别数量很多时。
from sklearn.preprocessing import OneHotEncoder
import numpy as np
# 创建示例数据
data = np.array(['Red', 'Green', 'Blue', 'Green', 'Red', 'Blue']).reshape(-1, 1)
# 实例化OneHotEncoder并进行编码
encoder = OneHotEncoder(sparse=False)
encoded_data = encoder.fit_transform(data)
print("独热编码后的数据:")
print(encoded_data)
print("类别顺序:", encoder.categories_)
二值化(Binarization)
二值化是一种将数值型特征转换为二进制值(0和1)的方法。它根据设定的阈值将大于阈值的值置为1,小于等于阈值的值置为0。
适用场景: 适用于需要将数值型特征转换为二进制特征的场景,特别是在逻辑回归等模型中,可以简化数据结构。
from sklearn.preprocessing import Binarizer
# 创建示例数据
data = np.array([[1.5], [2.5], [0.5], [1.0], [3.5]])
# 实例化Binarizer并进行二值化
binarizer = Binarizer(threshold=1.5)
binary_data = binarizer.fit_transform(data)
print("二值化后的数据:")
print(binary_data)
频数编码(Frequency Encoding)
频数编码是根据类别出现的频率进行编码,将类别变量转换为其在数据集中出现的频率或比例。频数编码保留了类别的原始频率信息,并将其映射为一个数值。
适用场景: 适用于类别数量较多且频率分布较为重要的场景。通过频数编码,可以捕捉类别的分布特性。
import pandas as pd
# 创建示例数据
data = pd.Series(['Red', 'Green', 'Blue', 'Green', 'Red', 'Blue', 'Red', 'Blue', 'Blue'])
# 计算类别频数并进行编码
frequency_encoding = data.value_counts(normalize=True)
encoded_data = data.map(frequency_encoding)
print("频数编码后的数据:")
print(encoded_data)
目标编码(Target Encoding)
目标编码是根据目标变量的均值或其他统计量对类别变量进行编码。它利用目标变量的信息对类别进行编码,这种方法在Kaggle比赛中非常流行。
适用场景: 适用于回归问题和分类问题中的类别变量编码,特别是在类别数量较多且类别之间的差异与目标变量相关性较强时。
注意事项: 需要注意防止数据泄漏(数据过拟合),通常会使用交叉验证或正则化来缓解这种问题。
# 创建示例数据
df = pd.DataFrame({
'Category': ['A', 'B', 'A', 'C', 'B', 'A', 'C', 'B'],
'Target': [10, 15, 14, 8, 13, 11, 9, 13]
})
# 计算目标编码
mean_encoding = df.groupby('Category')['Target'].mean()
df['Target_Encoded'] = df['Category'].map(mean_encoding)
print("目标编码后的数据:")
print(df)
哈希编码(Hash Encoding)
哈希编码是一种将类别变量通过哈希函数转换为固定长度整数向量的方法,通常用于高基数(类别数量多)的类别变量。
适用场景: 适用于处理高基数类别变量,特别是在需要节省内存和计算资源时,哈希编码是一个非常有效的方法。
注意事项: 哈希编码可能会引入哈希冲突,即不同的类别映射到相同的哈希值,但在大多数情况下,这种冲突的影响可以忽略不计。
from sklearn.feature_extraction import FeatureHasher
# 创建示例数据
data = [{'category': 'Red'}, {'category': 'Green'}, {'category': 'Blue'}, {'category': 'Green'}, {'category': 'Red'}]
# 实例化FeatureHasher并进行哈希编码
hasher = FeatureHasher(n_features=5, input_type='dict')
hashed_data = hasher.transform(data).toarray()
print("哈希编码后的数据:")
print(hashed_data)
分箱编码(Bin Encoding)
分箱编码是将数值型变量根据其分布划分为多个区间(箱),并将每个区间作为一个类别进行编码。这种编码方法可以将连续的数值型变量转换为类别型变量。
适用场景: 适用于需要将连续数值型变量转换为类别变量的场景,特别是在某些分类问题中,分箱编码能够提高模型的表现。
# 创建示例数据
data = np.array([5, 10, 15, 20, 25, 30])
# 分箱,将数据分为3个箱
bins = np.linspace(min(data), max(data), 4)
binned_data = np.digitize(data, bins)
print("分箱编码后的数据:")
print(binned_data)
数据预处理的方法还包括特征选择、特征提取、特征生成、数据降维、数据划分、数据增强、数据平滑、数据平衡等等,更多方法请关注下一期推送!
关注公众号,后台回复“数据预处理1”即可获得本文PDF!