23·数据处理进阶

特征工程:构建有意义的特征

feature-engineeringpreprocessing

第23课:特征工程:构建有意义的特征

学习目标

完成本课学习后,你将能够:

  1. 理解特征工程在机器学习流程中的核心作用和价值。
  2. 掌握数据清洗、特征创建、转换与缩放的常用方法。
  3. 学会从原始数据(特别是文本和时间数据)中提取有意义的新特征。
  4. 应用特征缩放技术(标准化、归一化)以改善模型性能。

核心概念

如果说机器学习模型是一个厨师,那么特征工程就是“食材预处理”和“创造新菜式”的过程。原始数据往往粗糙、杂乱,无法直接用于烹饪。特征工程的目标就是将原始数据转换成更能代表业务问题本质、更能帮助模型学习的特征。

这个过程没有唯一标准,它极度依赖于领域知识和直觉,是机器学习中最具创造性的部分。其主要工作包括:

  1. 数据清洗:处理缺失值、异常值、重复数据,这是所有工作的基础。
  2. 特征创建:基于现有数据,组合或计算出新的、信息量更丰富的特征。例如,从出生日期计算出“年龄”,从地址中提取出“省份”。
  3. 特征转换:改变特征的分布或尺度,使其更适合模型。例如,对极度偏斜的收入数据取对数。
  4. 特征缩放:将不同量纲的特征(如身高:米,体重:公斤,收入:元)缩放到相近的范围,这对基于距离和梯度的算法(如KNN、SVM、神经网络)至关重要。
  5. 文本与类别特征处理:将非数值数据(如文本、类别标签)转换为模型可理解的数值形式(这在上一课类别特征编码已初步涉及)。

代码示例

我们将通过一个模拟的“员工满意度”预测任务来演示特征工程的关键步骤。

import pandas as pd
import numpy as np
from datetime import datetime
from sklearn.preprocessing import StandardScaler, MinMaxScaler

# 1. 创建模拟原始数据集
data = {
    '员工ID': [1001, 1002, 1003, 1004, 1005],
    '入职日期': ['2020-01-15', '2019-06-20', '2021-03-10', '2018-11-05', '2022-07-01'],
    '部门': ['技术', '市场', '技术', '人事', '市场'],
    '月薪': [15000, 12000, 25000, 18000, np.nan], # 包含一个缺失值
    '绩效评分': [85, 92, 78, 88, 95], # 满分100
    '备注': ['项目成功,涨薪', '完成KPI', '有潜力,需培养', '沟通能力强', '新人表现优异']
}
df = pd.DataFrame(data)

print("=== 原始数据 ===")
print(df)
print("\n")

# 2. 数据清洗
# 处理缺失值:这里用同部门的中位数填充月薪
df['月薪'] = df.groupby('部门')['月薪'].transform(lambda x: x.fillna(x.median()))
# 检查并处理异常值(这里假设月薪>30000为异常,简单用中位数替代)
median_salary = df['月薪'].median()
df.loc[df['月薪'] > 30000, '月薪'] = median_salary

print("=== 清洗后数据(处理了缺失和异常值) ===")
print(df)
print("\n")

# 3. 特征创建
# 从日期创建新特征:工龄(年)
current_date = datetime.now()
df['入职日期'] = pd.to_datetime(df['入职日期'])
df['工龄_年'] = (current_date - df['入职日期']).dt.days / 365.25
df['工龄_年'] = df['工龄_年'].round(1)

# 从类别特征创建新特征:是否技术部
df['是否技术部'] = (df['部门'] == '技术').astype(int)

print("=== 特征创建后 ===")
print(df[['员工ID', '工龄_年', '是否技术部']])
print("\n")

# 4. 文本特征提取(简单示例)
# 从“备注”中提取关键词“成功”或“优秀”作为正面信号
keywords = ['成功', '优秀', '优异', '完成']
df['备注_正面信号'] = df['备注'].apply(lambda x: 1 if any(word in x for word in keywords) else 0)

print("=== 文本特征提取后 ===")
print(df[['员工ID', '备注', '备注_正面信号']])
print("\n")

# 5. 特征缩放
# 选择需要缩放的数值特征(月薪、绩效评分、工龄)
features_to_scale = ['月薪', '绩效评分', '工龄_年']

# 标准化 (Z-Score): 使数据均值为0,标准差为1
scaler_standard = StandardScaler()
df_scaled_standard = pd.DataFrame(
    scaler_standard.fit_transform(df[features_to_scale]),
    columns=[f'{col}_标准化' for col in features_to_scale]
)

# 归一化 (Min-Max): 将数据缩放到[0,1]区间
scaler_minmax = MinMaxScaler()
df_scaled_minmax = pd.DataFrame(
    scaler_minmax.fit_transform(df[features_to_scale]),
    columns=[f'{col}_归一化' for col in features_to_scale]
)

print("=== 特征缩放结果(标准化) ===")
print(df_scaled_standard)
print("\n=== 特征缩放结果(归一化) ===")
print(df_scaled_minmax)

代码解释

  • 我们首先模拟了一个有各种问题的原始数据集。
  • 清洗:处理了月薪的缺失值和假设的异常值。
  • 创建:从“入职日期”计算了“工龄_年”,从“部门”创建了“是否技术部”的二值特征。
  • 文本特征:通过关键词匹配,从文本“备注”中提取了“正面信号”这一数值特征。
  • 缩放:展示了标准化(StandardScaler)和归一化(MinMaxScaler)两种最常用的缩放方法。

实践练习

  1. 基础练习:使用上面的原始数据df,完成以下任务:

    • 处理“月薪”缺失值,改用全部员工的平均数填充。
    • 创建一个新特征“绩效等级”:评分>=90为“优秀”,80-89为“良好”,<80为“一般”。
    • 输出处理后的数据框,包含员工ID, 月薪, 绩效评分, 绩效等级
  2. 进阶练习:假设有一个包含“地址”列的数据:“北京市海淀区中关村”, “上海市浦东新区张江”

    • 编写一个函数,从地址中提取出“区”这一级信息(如“海淀区”、“浦东新区”)。
    • 将提取的“区”作为新特征添加到数据框中。
  3. 综合挑战:分析以下模拟数据集,设计你的特征工程流程。

    # 模拟电商用户数据
    ecom_data = pd.DataFrame({
        ‘用户ID’: [1, 2, 3, 4, 5],
        ‘注册日期’: [‘2023-01-10’, ‘2023-05-22’, ‘2022-12-01’, ‘2023-07-15’, ‘2023-03-08’],
        ‘最后登录’: [‘2024-02-20’, ‘2024-02-18’, ‘2024-02-19’, ‘2024-02-21’, ‘2024-01-10’],
        ‘总消费金额’: [580.5, 1200, 430, 880, 0],
        ‘购买次数’: [15, 32, 8, 24, 0],
        ‘投诉次数’: [0, 1, 2, 0, 3]
    })
    

    要求:至少创建3个新特征,并简要说明每个特征的业务含义。示例新特征:

    • 用户活跃天数 = 最后登录 - 注册日期
    • 平均客单价 = 总消费金额 / 购买次数
    • 投诉率 = 投诉次数 / 购买次数(处理除零问题)

常见错误

  1. 数据泄露:在特征创建或缩放时,使用了测试集或未来数据的信息。例如,用整个数据集的平均值填充缺失值后,再进行训练集/测试集划分。正确做法:先划分数据集,再使用训练集的统计量(均值、中位数等)去填充训练集和测试集。
  2. 盲目创造特征:不考虑业务逻辑,机械地组合所有特征,导致特征冗余(如同时创建了年龄出生年份年龄_分组)或引入噪声。应始终思考:“这个新特征真的能帮助模型理解目标吗?”
  3. 忽略缺失值和异常值:直接将包含NaN或极大极小值的数据丢给模型,可能导致模型训练失败或产生偏差。
  4. 缩放对象错误:对目标变量(y)进行了缩放(某些特定算法除外),或在需要归一化的场景使用了标准化,反之亦然。理解算法对数据分布的假设很重要。
  5. 忽略特征含义:仅仅进行数学变换,而丢失了特征的原始业务含义,导致模型结果难以解释。

小结

特征工程是将原始数据转化为模型“美食”的关键步骤。本节课我们学习了:

  • 数据清洗是基础,确保数据质量。
  • 特征创建是核心,需要结合业务知识,从现有数据中挖掘出更有代表性的信息(如从日期算工龄,从文本提关键信号)。
  • 特征转换与缩放是优化,旨在让数据格式更适合模型学习(标准化/归一化)。
  • 整个过程中必须警惕数据泄露,并始终思考特征的业务意义

记住,优秀的特征工程往往比选择一个复杂的模型更能提升效果。它是一项需要反复迭代和实验的技艺。在下一课中,我们将学习如何从众多创建好的特征中选择出最重要的那些。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「特征选择:过滤法、包装法与嵌入法」 以巩固所学知识。