在这里插入图片描述

1、 什么是特征工程

特征工程是从原始数据中提取、转换、选择最有效特征的过程。本质是将业务知识、领域洞察转化为机器可理解的数值表达。

  • 核心本质:数据预处理 + 领域知识建模 + 模型能力适配的桥梁
  • 关键认知:
    • 特征决定模型上限,算法只负责逼近这个上限
    • 好的特征让简单模型表现优异,坏的特征让复杂模型徒劳无功
    • 是"数据科学中最依赖人类智能"的环节

2、 特征工程都有哪些内容

维度 核心内容 价值
数据理解 探索性分析(EDA)、缺失值/异常值检测、分布分析 建立数据全景认知
特征构造 基础统计量、交互特征、时序特征、文本/图像特征转化 创造新的预测信号
特征转换 归一化/标准化、非线性变换(对数/box-cox)、编码处理 适配模型假设
特征选择 过滤法(filter)、包裹法(wrapper)、嵌入法(embedded) 去冗余、降维度
特征评估 重要性排序、稳定性分析、业务解释性验证 确保特征质量
特征降维 主成分分析(PCA)、线性判别分析(LDA)、(4)自编码器(Auto Encoder) 压缩实现数据

3、 特征工程常用的方法

3.1 数据清洗与处理

缺失值处理:删除、均值/中位数填充、KNN填充、预测模型填充
异常值处理:3σ原则、IQR箱线图、截断/替换
数据类型转换:数值型、类别型、时序型、文本型转换

3.2 特征构造方法

  • 统计特征:均值、方差、偏度、峰度、分位数
  • 交互特征:特征相乘/相除、多项式特征
  • 时序特征:时间差、周期性(星期/月份)、滚动窗口统计
  • 领域特征:
    • NLP:TF-IDF、Word2Vec、BERT embedding
    • CV:SIFT、HOG、CNN特征提取
    • 用户行为:RFM模型、留存率、活跃度

3.3 特征转换方法

  • 数值型:
    • Min-Max标准化:X’ = (X - min)/(max - min)
    • Z-score标准化:X’ = (X - μ)/σ
    • 对数变换:log(X + 1) 处理偏态分布
  • 类别型:
    • 标签编码(Label Encoding)
    • 独热编码(One-Hot Encoding)
    • 目标编码(Target Encoding)
    • 哈希编码(Hashing Encoding)

3.4 特征选择方法

  • 过滤法:相关系数、卡方检验、互信息、方差阈值
  • 包裹法:递归特征消除(RFE)、前向选择
  • 嵌入法:L1正则(Lasso)、树模型特征重要性、XGBoost/LightGBM的gain/cover

4、 特征工程demo

以房价预测为例,展示完整特征工程流程:

场景背景:假设我们有一个房价预测任务,原始数据包含了房屋的基本信息(面积、房间数、房龄、位置、风格)和售价。

Demo要解决的问题
原始数据通常存在以下问题,不能直接喂给模型:
数据不完整(有缺失值)
特征信息利用率低(仅使用原始字段)
数值范围差异大(面积几千,房间数个位)
类别特征无法计算(位置、风格是文本)
特征可能冗余(有些特征对预测无用)

import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.ensemble import RandomForestRegressor

# ===== 1. 数据加载与探索 =====
data = pd.read_csv('housing.csv')
print("原始数据形状:", data.shape)
print(data.describe())
print("缺失值统计:\n", data.isnull().sum())

# ===== 2. 缺失值处理 =====
# 数值型用中位数填充
num_cols = ['area', 'rooms', 'age']
for col in num_cols:
    data[col].fillna(data[col].median(), inplace=True)

# 类别型用众数填充
cat_cols = ['location', 'style']
for col in cat_cols:
    data[col].fillna(data[col].mode()[0], inplace=True)

# ===== 3. 特征构造 =====
# 构造交互特征
data['area_per_room'] = data['area'] / data['rooms']
data['age_category'] = pd.cut(data['age'], 
                              bins=[0, 5, 15, 100], 
                              labels=['new', 'mid', 'old'])

# 构造统计特征
data['price_per_area'] = data['price'] / data['area']

# ===== 4. 特征编码 =====
# 独热编码
encoder = OneHotEncoder(drop='first', sparse=False)
cat_encoded = encoder.fit_transform(data[['location', 'style']])
cat_feature_names = encoder.get_feature_names_out(['location', 'style'])

# 合并编码特征
num_features = data[['area', 'rooms', 'age', 'area_per_room']].values
X = np.hstack([num_features, cat_encoded])
y = data['price'].values

# ===== 5. 特征标准化 =====
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# ===== 6. 特征选择 =====
# 使用F回归选择Top 10特征
selector = SelectKBest(score_func=f_regression, k=10)
X_selected = selector.fit_transform(X_scaled, y)

# 获取选中的特征名称
selected_indices = selector.get_support(indices=True)
feature_names = list(num_cols + ['area_per_room'] + list(cat_feature_names))
selected_features = [feature_names[i] for i in selected_indices]

print("选中特征:", selected_features)

# ===== 7. 特征重要性评估 =====
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_selected, y)

importance = rf.feature_importances_
feature_importance_df = pd.DataFrame({
    'feature': selected_features,
    'importance': importance
}).sort_values('importance', ascending=False)

print("\n特征重要性排序:")
print(feature_importance_df)

输出示例:

选中特征: ['area', 'area_per_room', 'location_市中心', 'age', 'rooms', 'style_现代', 'location_近郊', 'location_学区', 'age_category_mid', 'style_欧式']

特征重要性排序:
           feature  importance
0             area    0.3521
1    area_per_room    0.2134
2  location_市中心    0.1567
3              age    0.1089
4            rooms    0.0675
5       style_现代    0.0456
...

代码分段解析

===== 第1步:数据加载与探索 =====
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.ensemble import RandomForestRegressor

# 读取原始数据
data = pd.read_csv('housing.csv')
print("原始数据形状:", data.shape)
print(data.describe())
print("缺失值统计:\n", data.isnull().sum())

解释说明:

  • 目的:拿到原始数据,先看看数据长什么样
  • 原始数据示例:
area rooms age location style price
120 3 10 市中心 现代 500万
80 2 NaN 近郊 中式 300万
  • data.isnull().sum():统计每列有多少空值,比如age列可能有5个缺失值
  • 这一步的意义:知己知彼,了解数据质量,为后续处理做准备
===== 第2步:缺失值处理 =====
# 数值型用中位数填充
num_cols = ['area', 'rooms', 'age']
for col in num_cols:
    data[col].fillna(data[col].median(), inplace=True)

# 类别型用众数填充
cat_cols = ['location', 'style']
for col in cat_cols:
    data[col].fillna(data[col].mode()[0], inplace=True)

解释说明:

  • 问题:原始数据中有空值,比如某套房子的age(房龄)未知

  • 为什么用中位数:中位数不受极端值影响,比如房龄大部分在10-20年,个别有100年的老房,中位数能代表"典型情况"

  • 为什么用众数:location是类别型(市中心/近郊/学区),不能计算平均,用出现最多的值填充最合理

  • 处理示例:

      处理前:
    
    area rooms age location
    120 3 NaN 市中心
     处理后:
    
    area rooms age location
    120 3 15 市中心
===== 第3步:特征构造 =====
# 构造交互特征
data['area_per_room'] = data['area'] / data['rooms']
data['age_category'] = pd.cut(data['age'], 
                              bins=[0, 5, 15, 100], 
                              labels=['new', 'mid', 'old'])

# 构造统计特征
data['price_per_area'] = data['price'] / data['area']

解释说明:

  • 这是特征工程的核心——创造新特征!

  • 特征1:area_per_room(人均面积)

    • 为什么:同样120平的房子,3室和2室的"宽敞程度"完全不同
    • 原始信息:只给了总面积和房间数
    • 新特征:120/3=40(每间房40平),80/2=40(每间房40平),虽然面积不同,但居住体验可能相近
  • 特征2:age_category(房龄分类)

    • 为什么:房龄是连续值(0-100年),模型可能难以捕捉"新、中、老"的跳跃性差异
    • 分类规则:
      • 0-5年 → new(新房)
      • 5-15年 → mid(次新房)
      • 15年以上 → old(老房)
  • 特征3:price_per_area(单价)

    • 为什么:这是目标衍生特征,可用于特征重要性评估或业务分析
  • 构造前后对比:
    原始特征:

    area rooms age
    120 3 10
    80 2 4

    新增特征:

    area_per_room age_category
    40 mid
    40 new
===== 第4步:特征编码 =====
# 独热编码
encoder = OneHotEncoder(drop='first', sparse=False)
cat_encoded = encoder.fit_transform(data[['location', 'style']])
cat_feature_names = encoder.get_feature_names_out(['location', 'style'])

# 合并编码特征
num_features = data[['area', 'rooms', 'age', 'area_per_room']].values
X = np.hstack([num_features, cat_encoded])
y = data['price'].values

解释说明:

  • 问题:机器学习模型只能处理数字,不能直接计算"市中心"、"中式"这些文本
  • 解决方案:独热编码(One-Hot Encoding),把类别转成二进制向量
  • 编码示例:

原始类别数据:

location style
市中心 现代
近郊 中式
学区 现代

独热编码后:

location_近郊 location_学区 location_市中心 style_中式 style_现代
0 0 1 0 1
1 0 0 1 0
0 1 0 0 1
  • 关键点:
    • drop=‘first’:去掉第一个类别(避免完全共线性,比如"location_市中心"为0时,自动表示其他位置)
    • sparse=False:返回稠密矩阵,方便后续合并
    • np.hstack:将数值特征和编码特征横向拼接成完整特征矩阵
X = [
  [area, rooms, age, area_per_room, location_近郊, location_学区, location_市中心, style_中式, style_现代],
  [120,  3,    10,  40,           0,             0,             1,              0,         1],
  [80,   2,    4,   40,           1,             0,             0,              1,         0],
  ...
]
===== 第5步:特征标准化 =====
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

解释说明:

  • 问题:特征之间量纲差异大,影响模型收敛

    • area范围:50-500(几百)
    • rooms范围:1-10(个位)
    • area_per_room范围:10-100(两位数)
    • 独热编码特征:0或1
  • 标准化公式:X’ = (X - μ) / σ

    • μ:均值
    • σ:标准差
  • 标准化示例:

    标准化前:

    area rooms location_市中心
    120 3 1
    80 2 0

    标准化后(假设):

    area rooms location_市中心
    1.2 0.5 1.0
    -0.8 -1.5 -1.0

现在所有特征都在相近的数值范围内,模型训练更稳定

===== 第6步:特征选择 =====
# 使用F回归选择Top 10特征
selector = SelectKBest(score_func=f_regression, k=10)
X_selected = selector.fit_transform(X_scaled, y)

# 获取选中的特征名称
selected_indices = selector.get_support(indices=True)
feature_names = list(num_cols + ['area_per_room'] + list(cat_feature_names))
selected_features = [feature_names[i] for i in selected_indices]

print("选中特征:", selected_features)

解释说明:

  • 问题:可能构造了太多特征(比如有50个),其中有些对房价预测没有帮助,反而增加计算成本和过拟合风险

  • 解决方案:特征选择,保留最有用的特征

  • F回归原理:

    • 计算每个特征与目标变量(房价)的F统计量
    • F值越大,说明该特征与房价相关性越强
  • 选择示例:

所有特征及其F分数:

特征 F分数 排名
area 1250 1
area_per_room 980 2
location_市中心 850 3
age 720 4
style_现代 650 5
style_欧式 45 12

选择Top 10后,丢弃最后2个弱相关特征

===== 第7步:特征重要性评估 =====
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_selected, y)

importance = rf.feature_importances_
feature_importance_df = pd.DataFrame({
    'feature': selected_features,
    'importance': importance
}).sort_values('importance', ascending=False)

print("\n特征重要性排序:")
print(feature_importance_df)

解释说明:

  • 目的:用随机森林模型评估每个特征的"贡献度"
  • 原理:随机森林在训练时会随机选择特征,如果一个特征经常被用于分割节点且能显著降低- 误差,说明这个特征很重要
  • 输出示例:
特征重要性排序:
           feature  importance
0             area    0.3521    ← 面积最重要(占35.21%1    area_per_room    0.2134    ← 人均面积次重要
2  location_市中心    0.1567    ← 位置也很关键
3              age    0.1089
4            rooms    0.0675
5       style_现代    0.0456
...
  • 业务解读:
    • 面积决定了房子价值的基础(35%)
    • 单价(人均面积)反映了性价比(21%)
    • 地段是核心溢价因素(16%)
    • 房龄、房间数、风格是辅助因素
完整流程

原始数据

数据探索
了解数据质量、缺失情况

缺失值处理
中位数/众数填充空值

特征构造
创造新特征
交互、分类、统计

特征编码
类别→数值
独热编码

特征标准化
统一数值范围

特征选择
去除冗余特征

特征评估
了解哪些特征最重要

高质量特征矩阵

喂给机器学习模型

5、 本章节速记

特征工程分四步,洗转选评不马虎;
缺失异常先处理,交互时序造新图;
归一标准化转换,过滤包裹嵌入法;
业务知识是灵魂,模型上限靠它出。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐