有效的数据准备对于构建强大的机器学习模型至关重要。本文档总结并阐述了为监督和非监督学习任务准备数据的关键技术。
有两种数据类型。定性数据描述对象的特征,而定量数据描述对象的数量。
一旦从现实世界获取数据(数据收集),我们需要探索和总结数据(数据分析)。在这个阶段通常使用可视化来理解数据分布(数据分散度量)。
可视化数据有很多方法。以下是一些常见的方法。
在示例中,我们将使用 matplotlib
库来绘制它们。
处理缺失值和异常值是数据准备的重要步骤。现实世界的数据往往不完美。缺失数据、异常值和其他问题需要在此步骤中解决,以实现有效的机器学习。
删除通常在删除一些数据行不会损失太多信息时应用。这通常与 dropna()
方法相关。另一方面,插补可能是一种更实际的方法,通过为缺失数据提供人工值来保留重要数据属性,同时不影响数据分布。
插补是用替代值替换缺失数据的过程。这很关键,因为大多数机器学习算法无法直接处理缺失值。
异常值是与其他观测值显著不同的数据点。可能由测量错误、数据输入错误或自然变异引起。
许多机器学习算法在特征具有相似尺度时表现更好或收敛更快。缩放还确保不同量级的特征不会主导模型学习。
x ′ = x − μ σ x' = \frac{x - \mu}{\sigma} x′=σx−μ
x ′ = x − x min x max − x min x' = \frac{x - x_{\text{min}}}{x_{\text{max}} - x_{\text{min}}} x′=xmax−xminx−xmin
减少特征数量,同时保留重要信息。
选择最相关的特征子集以:
任务 | 技术 |
---|---|
识别变量类型 | 名义、有序、区间、比率 |
总结数值数据 | 均值、中位数、标准差、四分位距 |
可视化数据 | 直方图、箱线图、散点图 |
处理缺失值 | 删除、插补、预测 |
处理异常值 | 移除、封顶、调查 |
缩放特征 | 标准化、归一化 |
降维 | 主成分分析、奇异值分解 |
选择特征 | 过滤、包装、嵌入方法 |
此笔记本使用关于汽车属性和燃油效率的假数据集说明数据准备的关键技术点。
# 导入所需库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest, f_regression, RFE
from sklearn.linear_model import LinearRegression
# 假数据集
data = {
"car_name": ["car_a", "car_b", "car_c", "car_d", "car_e", "car_f"],
"cylinders": [4, 6, 8, 4, 4, 8],
"displacement": [140, 200, 360, 150, 130, 3700],
"horsepower": [90, 105, 215, 92, np.nan, 220], # np (numpy - 数字Python - 用于科学计算的库。nan: 非数字/空值)
"weight": [2400, 3000, 4300, 2500, 2200, 4400],
"acceleration": [15.5, 14.0, 12.5, 16.0, 15.0, 11.0],
"model_year": [80, 78, 76, 82, 81, 77],
"origin": [1, 1, 1, 2, 3, 1],
"mpg": [30.5, 24.0, 13.0, 29.5, 32.0, 10.0]
}
df = pd.DataFrame(data)
df
car_name | cylinders | displacement | horsepower | weight | acceleration | model_year | origin | mpg | |
---|---|---|---|---|---|---|---|---|---|
0 | car_a | 4 | 140 | 90.0 | 2400 | 15.5 | 80 | 1 | 30.5 |
1 | car_b | 6 | 200 | 105.0 | 3000 | 14.0 | 78 | 1 | 24.0 |
2 | car_c | 8 | 360 | 215.0 | 4300 | 12.5 | 76 | 1 | 13.0 |
3 | car_d | 4 | 150 | 92.0 | 2500 | 16.0 | 82 | 2 | 29.5 |
4 | car_e | 4 | 130 | NaN | 2200 | 15.0 | 81 | 3 | 32.0 |
5 | car_f | 8 | 3700 | 220.0 | 4400 | 11.0 | 77 | 1 | 10.0 |
car_name
:名义(分类)cylinders
, origin
:有序/分类displacement
, horsepower
, weight
, acceleration
, mpg
:比率(数值)model_year
:区间# 1. 处理缺失值示例
print("=== 插补前的缺失值 ===")
print(df.isna().sum())
# 均值插补
mean_imputer = SimpleImputer(strategy='mean')
df['horsepower_mean'] = mean_imputer.fit_transform(df[['horsepower']])
# 基于组的插补
group_means = df.groupby('cylinders')['horsepower'].transform('mean')
df['horsepower_group'] = df['horsepower'].fillna(group_means)
# KNN插补
knn_imputer = KNNImputer(n_neighbors=2)
df['horsepower_knn'] = knn_imputer.fit_transform(df[['horsepower']])
print("\n=== 插补后 ===")
df[['horsepower', 'horsepower_mean', 'horsepower_group', 'horsepower_knn']]
=== 插补前的缺失值 ===
car_name 0
cylinders 0
displacement 0
horsepower 1
weight 0
acceleration 0
model_year 0
origin 0
mpg 0
dtype: int64
=== 插补后 ===
horsepower | horsepower_mean | horsepower_group | horsepower_knn | |
---|---|---|---|---|
0 | 90.0 | 90.0 | 90.0 | 90.0 |
1 | 105.0 | 105.0 | 105.0 | 105.0 |
2 | 215.0 | 215.0 | 215.0 | 215.0 |
3 | 92.0 | 92.0 | 92.0 | 92.0 |
4 | NaN | 144.4 | 91.0 | 144.4 |
5 | 220.0 | 220.0 | 220.0 | 220.0 |
# 2. 处理异常值示例
def detect_and_handle_outliers(df, column):
# 计算四分位距
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 检测异常值
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
print(f'在 {column} 中检测到 {len(outliers)} 个异常值')
# 可视化前后对比
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
sns.boxplot(y=df[column])
plt.title(f'原始 {column}')
# 封顶异常值
df[f'{column}_capped'] = np.where(df[column] > upper_bound, upper_bound,
np.where(df[column] < lower_bound, lower_bound, df[column]))
plt.subplot(1, 2, 2)
sns.boxplot(y=df[f'{column}_capped'])
plt.title(f'封顶后的 {column}')
plt.tight_layout()
plt.show()
return df
df = detect_and_handle_outliers(df, 'displacement')
在 displacement 中检测到 1 个异常值
# 3. 特征缩放示例
# 原始数据
numeric_cols = ['weight', 'acceleration', 'displacement']
print('原始数据:')
print(df[numeric_cols].head())
# 标准化
scaler = StandardScaler()
df_std = df.copy()
df_std[numeric_cols] = scaler.fit_transform(df[numeric_cols])
# 最小-最大缩放
minmax = MinMaxScaler()
df_minmax = df.copy()
df_minmax[numeric_cols] = minmax.fit_transform(df[numeric_cols])
print('\n标准化数据 (均值=0, 标准差=1):')
print(df_std[numeric_cols].head())
print('最小-最大缩放数据 (范围 [0,1]):')
print(df_minmax[numeric_cols].head())
原始数据:
weight acceleration displacement
0 2400 15.5 140
1 3000 14.0 200
2 4300 12.5 360
3 2500 16.0 150
4 2200 15.0 130
标准化数据 (均值=0, 标准差=1):
weight acceleration displacement
0 -0.820462 0.854242 -0.489225
1 -0.149175 0.000000 -0.443360
2 1.305280 -0.854242 -0.321054
3 -0.708580 1.138990 -0.481581
4 -1.044224 0.569495 -0.496869
最小-最大缩放数据 (范围 [0,1]):
weight acceleration displacement
0 0.090909 0.9 0.002801
1 0.363636 0.6 0.019608
2 0.954545 0.3 0.064426
3 0.136364 1.0 0.005602
4 0.000000 0.8 0.000000
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(8, 5))
sns.boxplot(data=df[['mpg', 'weight', 'acceleration']])
plt.title("数值特征的箱线图")
plt.show()
df[['acceleration']].hist(bins=5, figsize=(6, 4))
plt.title("加速直方图")
plt.show()
sns.scatterplot(x='weight', y='mpg', data=df)
plt.title("散点图:重量 vs 每加仑英里数")
plt.show()
pd.crosstab(df['origin'], df['cylinders'])
cylinders | 4 | 6 | 8 |
---|---|---|---|
origin | |||
1 | 1 | 1 | 2 |
2 | 1 | 0 | 0 |
3 | 1 | 0 | 0 |
# 4. 降维示例
# 准备PCA数据
X = df[['weight', 'acceleration', 'displacement_capped']]
y = df['mpg']
# 首先标准化数据
X_scaled = StandardScaler().fit_transform(X)
# 应用PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# 创建主成分的新数据框
df_pca = pd.DataFrame(data=X_pca, columns=['PC1', 'PC2'])
df_pca['mpg'] = y.values
# 绘制结果
plt.figure(figsize=(8, 6))
scatter = plt.scatter(df_pca['PC1'], df_pca['PC2'], c=df_pca['mpg'], cmap='viridis')
plt.xlabel('第一主成分')
plt.ylabel('第二主成分')
plt.colorbar(scatter, label='每加仑英里数')
plt.title('汽车特征的PCA')
plt.show()
print(f'解释方差比例: {pca.explained_variance_ratio_}')
print(f'总解释方差: {sum(pca.explained_variance_ratio_):.2f}%')
解释方差比例: [0.95929265 0.02632386]
总解释方差: 0.99%
如果通过特征重要性技术发现 car_name
或 model_year
无关,我们可能会删除它们。