数据预处理与特征工程 数据是机器学习的燃料,而特征工程是提炼这些燃料的关键工艺

数据预处理与特征工程

学习目标

完成本节后,你将能够:

  • 使用Pandas进行数据加载和基础处理
  • 识别和处理数据质量问题(缺失值、异常值等)
  • 掌握特征工程的各种技术(编码、缩放、创建等)
  • 构建端到端的数据处理Pipeline

先修知识

学习本节内容需要:

  • Python基础编程
  • Pandas库的基本使用
  • 机器学习基础概念
  • 基本的统计知识

Pandas数据处理基础

数据读取与查看

数据分析的第一步是了解数据的基本情况,包括数据结构、特征类型和统计特征。

import pandas as pd
import numpy as np

# 读取数据
df = pd.read_csv('data.csv')

# 基本信息查看
print("数据基本信息:")
print(df.info())

# 数据预览
print("\n前5行数据:")
print(df.head())

# 基本统计信息
print("\n基本统计信息:")
print(df.describe())

数据选择与过滤

熟练的数据操作能力是数据分析的基础。

# 列选择
selected_columns = df[['column1', 'column2']]

# 条件过滤
filtered_data = df[df['age'] > 25]

# 多条件过滤
complex_filter = df[(df['age'] > 25) & (df['salary'] > 50000)]

# 索引操作
df.loc[0:5, 'column1':'column3']  # 标签索引
df.iloc[0:5, 0:3]                 # 位置索引

数据转换

数据转换是将原始数据转化为更适合分析的形式。

# 类型转换
df['age'] = df['age'].astype('int32')

# 重命名列
df = df.rename(columns={'old_name': 'new_name'})

# 排序
df_sorted = df.sort_values('column1', ascending=False)

# 分组操作
grouped = df.groupby('category').agg({
    'value': ['mean', 'count'],
    'other_value': 'sum'
})

数据清洗

缺失值处理

缺失值是数据分析中最常见的问题之一,需要谨慎处理。

# 检查缺失值
missing_values = df.isnull().sum()
missing_percentage = (df.isnull().sum() / len(df)) * 100

# 处理缺失值
# 删除
df_cleaned = df.dropna()  # 删除含有缺失值的行
df_cleaned = df.dropna(subset=['important_column'])  # 特定列

# 填充
df['column'].fillna(df['column'].mean())  # 均值填充
df['column'].fillna(df['column'].median())  # 中位数填充
df['column'].fillna(df['column'].mode()[0])  # 众数填充
df['column'].fillna(method='ffill')  # 前向填充
df['column'].fillna(method='bfill')  # 后向填充

异常值检测与处理

异常值可能是错误数据,也可能是重要的异常情况。

from scipy import stats

def detect_outliers_zscore(data, threshold=3):
    """
    使用Z-score方法检测异常值
    
    参数:
        data: 数值型数据
        threshold: Z-score阈值,默认为3
    
    返回:
        布尔数组,True表示异常值
    """
    z_scores = stats.zscore(data)
    return abs(z_scores) > threshold

def detect_outliers_iqr(data):
    """
    使用IQR方法检测异常值
    
    参数:
        data: 数值型数据
    
    返回:
        布尔数组,True表示异常值
    """
    Q1 = data.quantile(0.25)
    Q3 = data.quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    return (data < lower_bound) | (data > upper_bound)

def cap_outliers(data):
    """
    将异常值截断到上下限范围内
    
    参数:
        data: 数值型数据
    
    返回:
        处理后的数据
    """
    Q1 = data.quantile(0.25)
    Q3 = data.quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    return np.clip(data, lower_bound, upper_bound)

# 使用示例
# 删除异常值
df = df[~detect_outliers_zscore(df['column'])]

# 截断异常值
df['column'] = cap_outliers(df['column'])

数据一致性检查

确保数据的质量和一致性是数据清洗的重要部分。

# 重复值检查
duplicates = df.duplicated().sum()
df_unique = df.drop_duplicates()

def check_range(data, column, min_val, max_val):
    """
    检查数据是否在指定范围内
    
    参数:
        data: DataFrame
        column: 要检查的列名
        min_val: 最小允许值
        max_val: 最大允许值
    
    返回:
        超出范围的数据
    """
    invalid = data[(data[column] < min_val) | (data[column] > max_val)]
    return invalid

def check_format(data, column, pattern):
    """
    检查数据是否符合指定格式
    
    参数:
        data: DataFrame
        column: 要检查的列名
        pattern: 正则表达式模式
    
    返回:
        不符合格式的数据
    """
    import re
    invalid = data[~data[column].str.match(pattern)]
    return invalid

特征工程

特征缩放

不同尺度的特征可能会影响模型性能,需要进行适当的缩放。

from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler

# 标准化 (Z-score标准化)
scaler = StandardScaler()
df_scaled = scaler.fit_transform(df)

# 归一化 (Min-Max缩放)
min_max_scaler = MinMaxScaler()
df_normalized = min_max_scaler.fit_transform(df)

# 稳健缩放 (处理异常值)
robust_scaler = RobustScaler()
df_robust = robust_scaler.fit_transform(df)

特征编码

将分类特征转换为数值形式,使机器学习算法能够处理。

from sklearn.preprocessing import LabelEncoder, OneHotEncoder

# 标签编码
label_encoder = LabelEncoder()
df['category_encoded'] = label_encoder.fit_transform(df['category'])

# One-Hot编码
onehot_encoder = OneHotEncoder(sparse=False)
onehot_encoded = onehot_encoder.fit_transform(df[['category']])

# 自定义映射
mapping = {'low': 0, 'medium': 1, 'high': 2}
df['category_mapped'] = df['category'].map(mapping)

# 频率编码
frequency_map = df['category'].value_counts(normalize=True).to_dict()
df['category_freq'] = df['category'].map(frequency_map)

特征创建与转换

创建新特征或转换现有特征可以帮助模型捕捉更多信息。

# 多项式特征
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2)
X_poly = poly.fit_transform(X)

# 交互特征
df['interaction'] = df['feature1'] * df['feature2']

# 时间特征
df['date'] = pd.to_datetime(df['date'])
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day
df['dayofweek'] = df['date'].dt.dayofweek

# 分箱
df['age_group'] = pd.qcut(df['age'], q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])

实战:构建数据处理Pipeline

项目描述

  • 目标:构建一个端到端的数据处理pipeline
  • 数据:包含数值和类别特征的数据集
  • 技术要点:数据清洗、特征工程、Pipeline构建

完整代码实现

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.ensemble import RandomForestClassifier

# 定义数值和类别特征
numeric_features = ['age', 'salary']
categorical_features = ['department', 'position']

# 数值特征处理
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# 类别特征处理
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('onehot', OneHotEncoder(drop='first'))
])

# 组合转换器
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)
    ])

# 创建完整pipeline
pipeline = Pipeline(steps=[
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier())
])

# 训练pipeline
pipeline.fit(X_train, y_train)

# 预测
y_pred = pipeline.predict(X_test)

练习与作业

  • 基础练习:

    • 使用Pandas读取一个CSV文件
    • 进行基本的数据探索
    • 处理文件中的缺失值
  • 提高练习:

    • 实现一个函数,能够自动检测和处理异常值
    • 对数值特征进行不同方式的缩放,比较效果
  • 挑战练习:

    • 构建一个完整的数据处理pipeline
    • 包含缺失值处理、异常值处理、特征工程等步骤
    • 使用交叉验证评估pipeline的效果

常见问题

Q1: 如何选择合适的缺失值处理方法? A1: 需要考虑以下因素:

  • 缺失值的比例
  • 缺失的原因
  • 数据的分布特征
  • 业务场景的要求

Q2: 什么时候应该使用One-Hot编码,什么时候使用标签编码? A2:

  • One-Hot编码适用于:
    • 类别之间没有顺序关系
    • 类别数量较少
    • 模型对特征独立性要求高
  • 标签编码适用于:
    • 类别之间有顺序关系
    • 类别数量很多
    • 树模型等对特征独立性要求不高的模型

小测验

  • 以下哪些是处理缺失值的有效方法?为什么?

    • 删除缺失值
    • 均值填充
    • 中位数填充
    • 模型预测填充
  • 解释为什么要进行特征缩放?不同的缩放方法有什么区别?

  • 在实际项目中,如何选择合适的特征工程方法?需要考虑哪些因素?

扩展阅读

下一步学习

  • 机器学习算法基础
  • 模型评估与选择
  • 模型调优技术
  • 特征选择方法