第203集:特征工程

1. 特征工程概述

1.1 什么是特征工程

特征工程(Feature Engineering)是指从原始数据中提取、转换、选择和创建有用特征的过程,以提高机器学习模型的性能。它是将原始数据转换为适合模型训练的特征表示的艺术和科学。

1.2 特征工程的重要性

  • 提高模型性能:好的特征可以显著提升模型的准确性和泛化能力
  • 降低计算复杂度:通过选择和提取特征减少数据维度
  • 增强模型可解释性:高质量的特征使模型结果更容易理解
  • 解决数据质量问题:处理数据中的噪声、异常值和缺失值
  • 适应模型要求:将原始数据转换为模型可接受的格式

1.3 特征工程的主要步骤

  1. 特征理解:分析原始数据的含义和特点
  2. 特征提取:从原始数据中提取有用信息
  3. 特征转换:将特征转换为适合模型的形式
  4. 特征选择:选择最相关的特征子集
  5. 特征创建:创建新的组合特征
  6. 特征评估:评估特征对模型性能的贡献

2. 特征选择

2.1 特征选择的目的

  • 减少特征数量,降低模型复杂度
  • 提高模型训练效率
  • 减少过拟合风险
  • 增强模型可解释性

2.2 特征选择方法分类

2.2.1 过滤法(Filter Methods)

基于特征的统计特性进行选择,不依赖具体模型:

# 皮尔逊相关系数
from sklearn.feature_selection import SelectKBest, f_regression, chi2

# 回归问题
selector = SelectKBest(f_regression, k=10)
X_new = selector.fit_transform(X, y)

# 分类问题
selector = SelectKBest(chi2, k=10)
X_new = selector.fit_transform(X, y)

# 互信息
from sklearn.feature_selection import mutual_info_regression, mutual_info_classif

# 回归问题的互信息
mutual_info = mutual_info_regression(X, y)
selected_features = X.columns[np.argsort(mutual_info)[-10:]]

# 分类问题的互信息
mutual_info = mutual_info_classif(X, y)
selected_features = X.columns[np.argsort(mutual_info)[-10:]]

2.2.2 包裹法(Wrapper Methods)

基于模型性能进行特征选择,考虑特征间的交互关系:

# 递归特征消除
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier()
rfe = RFE(estimator=model, n_features_to_select=10, step=1)
X_rfe = rfe.fit_transform(X, y)

# 显示选择的特征
selected_features = X.columns[rfe.support_]

# 使用交叉验证的递归特征消除
from sklearn.feature_selection import RFECV

rfecv = RFECV(estimator=model, step=1, cv=5, scoring='accuracy')
X_rfecv = rfecv.fit_transform(X, y)

# 最优特征数量
print(f"最优特征数量: {rfecv.n_features_}")

2.2.3 嵌入法(Embedded Methods)

结合了过滤法和包裹法的优点,在模型训练过程中进行特征选择:

# L1正则化(Lasso)
from sklearn.linear_model import Lasso

lasso = Lasso(alpha=0.1)
lasso.fit(X, y)

# 非零系数对应的特征
selected_features = X.columns[lasso.coef_ != 0]

# 决策树特征重要性
from sklearn.tree import DecisionTreeClassifier

model = DecisionTreeClassifier()
model.fit(X, y)

# 特征重要性
feature_importances = model.feature_importances_
selected_features = X.columns[np.argsort(feature_importances)[-10:]]

# 随机森林特征重要性
from sklearn.ensemble import RandomForestRegressor

model = RandomForestRegressor()
model.fit(X, y)
feature_importances = model.feature_importances_

2.2.4 基于模型的特征选择

# 基于梯度提升的特征重要性
from sklearn.ensemble import GradientBoostingClassifier

model = GradientBoostingClassifier()
model.fit(X, y)
feature_importances = model.feature_importances_

# 显示特征重要性
for feature, importance in zip(X.columns, feature_importances):
    print(f"{feature}: {importance:.4f}")

3. 特征提取

3.1 特征提取的定义

从原始数据中提取新的特征,通常用于降维和提取关键信息。

3.2 常见的特征提取方法

3.2.1 主成分分析(PCA)

from sklearn.decomposition import PCA

# 创建PCA模型
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)

# 查看解释方差比
print(f"解释方差比: {pca.explained_variance_ratio_}")
print(f"累计解释方差: {sum(pca.explained_variance_ratio_):.2f}")

# 选择能够解释95%方差的主成分数量
pca = PCA(n_components=0.95)
X_pca = pca.fit_transform(X)
print(f"选择的主成分数量: {pca.n_components_}")

3.2.2 线性判别分析(LDA)

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

lda = LinearDiscriminantAnalysis(n_components=2)
X_lda = lda.fit_transform(X, y)  # LDA需要目标变量

# 查看解释方差比
print(f"解释方差比: {lda.explained_variance_ratio_}")

3.2.3 独立成分分析(ICA)

from sklearn.decomposition import FastICA

ica = FastICA(n_components=2, random_state=42)
X_ica = ica.fit_transform(X)

3.2.4 特征提取与降维比较

方法 适用场景 优点 缺点
PCA 数据降维,去除相关性 计算高效,可解释性强 假设数据正态分布
LDA 分类问题的降维 考虑类别信息,适合分类 只适用于分类问题
ICA 信号分离,独立成分提取 不假设高斯分布 结果解释性差

4. 特征转换

4.1 特征转换的目的

将特征转换为适合模型的形式,包括:

  • 非线性关系线性化
  • 处理异方差性
  • 稳定方差
  • 提高模型收敛速度

4.2 常见的特征转换方法

4.2.1 数学转换

# 对数转换
import numpy as np

df['log_feature'] = np.log(df['feature'] + 1)  # +1避免0值

# 平方根转换
df['sqrt_feature'] = np.sqrt(df['feature'])

# 幂转换
df['power_feature'] = df['feature'] ** 2

# 指数转换
df['exp_feature'] = np.exp(df['feature'])

4.2.2 Box-Cox转换

from scipy.stats import boxcox

# Box-Cox转换
df['boxcox_feature'], lambda_value = boxcox(df['positive_feature'])  # 要求特征值为正
print(f"Box-Cox转换的lambda值: {lambda_value}")

4.2.3 Yeo-Johnson转换

from sklearn.preprocessing import PowerTransformer

# Yeo-Johnson转换(支持负值)
transformer = PowerTransformer(method='yeo-johnson')
df['yeojohnson_feature'] = transformer.fit_transform(df[['feature']])

4.2.4 离散化(分箱)

# 等宽分箱
df['equal_width'] = pd.cut(df['feature'], bins=5, labels=False)

# 等频分箱
df['equal_freq'] = pd.qcut(df['feature'], q=5, labels=False)

# 自定义分箱
bins = [0, 20, 40, 60, 80, 100]
labels = ['低', '中低', '中', '中高', '高']
df['custom_bins'] = pd.cut(df['feature'], bins=bins, labels=labels)

4.2.5 文本特征转换

# 词袋模型
from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer(max_features=1000)
X_bow = vectorizer.fit_transform(texts)

# TF-IDF
from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer(max_features=1000)
X_tfidf = vectorizer.fit_transform(texts)

5. 特征创建

5.1 特征创建的定义

通过组合或转换现有特征创建新的有意义的特征。

5.2 常见的特征创建方法

5.2.1 统计特征

# 移动平均线
df['moving_average'] = df['value'].rolling(window=7).mean()

# 累计和
df['cumulative_sum'] = df['value'].cumsum()

# 波动率
df['volatility'] = df['value'].rolling(window=7).std()

# 最值
df['max_7d'] = df['value'].rolling(window=7).max()
df['min_7d'] = df['value'].rolling(window=7).min()

5.2.2 交互特征

# 特征相乘
df['feature1_x_feature2'] = df['feature1'] * df['feature2']

# 特征相加
df['feature1_plus_feature2'] = df['feature1'] + df['feature2']

# 特征相除
df['feature1_div_feature2'] = df['feature1'] / (df['feature2'] + 1e-5)  # 避免除以0

# 多项式特征
from sklearn.preprocessing import PolynomialFeatures

poly = PolynomialFeatures(degree=2, interaction_only=False, include_bias=False)
X_poly = poly.fit_transform(X)
X_poly_df = pd.DataFrame(X_poly, columns=poly.get_feature_names_out(X.columns))

5.2.3 时间特征

# 日期时间特征提取
from datetime import datetime

df['date'] = pd.to_datetime(df['timestamp'])
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day
df['hour'] = df['date'].dt.hour
df['minute'] = df['date'].dt.minute
df['second'] = df['date'].dt.second
df['weekday'] = df['date'].dt.weekday
df['is_weekend'] = df['weekday'].isin([5, 6]).astype(int)
df['is_holiday'] = df['date'].dt.strftime('%Y-%m-%d').isin(holidays).astype(int)

5.2.4 地理空间特征

# 经纬度距离计算
from geopy.distance import geodesic

def calculate_distance(row):
    point1 = (row['lat1'], row['lon1'])
    point2 = (row['lat2'], row['lon2'])
    return geodesic(point1, point2).kilometers

df['distance_km'] = df.apply(calculate_distance, axis=1)

# 方位角计算
import numpy as np

def calculate_bearing(lat1, lon1, lat2, lon2):
    dlon = np.radians(lon2 - lon1)
    lat1 = np.radians(lat1)
    lat2 = np.radians(lat2)
    
    y = np.sin(dlon) * np.cos(lat2)
    x = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(dlon)
    bearing = np.arctan2(y, x)
    bearing = np.degrees(bearing)
    bearing = (bearing + 360) % 360
    
    return bearing

df['bearing'] = df.apply(lambda row: calculate_bearing(row['lat1'], row['lon1'], row['lat2'], row['lon2']), axis=1)

6. 特征工程的最佳实践

6.1 数据理解

  • 深入了解数据的业务背景和含义
  • 分析数据的分布、相关性和异常值
  • 与领域专家合作获取专业知识

6.2 特征设计原则

  • 简单性:优先选择简单直观的特征
  • 相关性:选择与目标变量相关的特征
  • 独立性:避免高度相关的特征
  • 稳定性:特征在不同数据集上应保持稳定
  • 可解释性:特征应易于理解和解释

6.3 避免过拟合

  • 不要创建过多的特征
  • 使用交叉验证评估特征
  • 考虑特征的泛化能力
  • 对特征数量进行控制

6.4 特征评估

  • 使用多种指标评估特征的重要性
  • 可视化特征与目标变量的关系
  • 通过模型性能验证特征的有效性
  • 定期重新评估和更新特征

6.5 自动化特征工程

# 使用Feature-engine库
from feature_engine import feature_creation
from feature_engine import feature_selection
from feature_engine import transformation

# 特征创建
transformer = feature_creation.MathFeatures(
    variables=['feature1', 'feature2'],
    func=['sum', 'product', 'ratio']
)
df = transformer.fit_transform(df)

# 使用TPOT自动化机器学习
from tpot import TPOTRegressor
from tpot import TPOTClassifier

# 回归问题
tpot = TPOTRegressor(verbosity=2, max_time_mins=60, n_jobs=-1)
tpot.fit(X_train, y_train)

tpot.export('tpot_pipeline.py')

7. 特征工程案例分析

7.1 电商用户行为分析

# 加载数据
import pandas as pd

# 假设我们有用户购买记录数据
user_data = pd.read_csv('user_purchase_data.csv')

# 1. 创建时间特征
user_data['purchase_date'] = pd.to_datetime(user_data['purchase_timestamp'])
user_data['purchase_month'] = user_data['purchase_date'].dt.month
user_data['purchase_weekday'] = user_data['purchase_date'].dt.weekday
user_data['purchase_hour'] = user_data['purchase_date'].dt.hour

# 2. 创建用户行为特征
# 计算每个用户的总购买次数
user_purchase_count = user_data.groupby('user_id')['purchase_id'].count().reset_index()
user_purchase_count.columns = ['user_id', 'total_purchases']

# 计算每个用户的总消费金额
user_total_spent = user_data.groupby('user_id')['amount'].sum().reset_index()
user_total_spent.columns = ['user_id', 'total_spent']

# 计算每个用户的平均购买金额
user_avg_spent = user_data.groupby('user_id')['amount'].mean().reset_index()
user_avg_spent.columns = ['user_id', 'avg_spent_per_purchase']

# 计算最近一次购买到当前的天数
current_date = user_data['purchase_date'].max()
user_last_purchase = user_data.groupby('user_id')['purchase_date'].max().reset_index()
user_last_purchase['days_since_last_purchase'] = (current_date - user_last_purchase['purchase_date']).dt.days

# 3. 合并特征
user_features = user_purchase_count.merge(user_total_spent, on='user_id')
user_features = user_features.merge(user_avg_spent, on='user_id')
user_features = user_features.merge(user_last_purchase[['user_id', 'days_since_last_purchase']], on='user_id')

# 4. 特征选择
from sklearn.feature_selection import mutual_info_regression

# 假设我们的目标是预测用户未来消费金额
target = user_features['total_spent']
features = user_features.drop(['user_id', 'total_spent'], axis=1)

# 计算互信息
mutual_info = mutual_info_regression(features, target)
feature_importance = pd.DataFrame({'feature': features.columns, 'importance': mutual_info})
feature_importance = feature_importance.sort_values('importance', ascending=False)

print("特征重要性排序:")
print(feature_importance)

# 5. 特征转换
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
scaled_features = scaler.fit_transform(features)
scaled_features_df = pd.DataFrame(scaled_features, columns=features.columns)

# 6. 保存特征
user_features_final = pd.concat([user_features['user_id'], scaled_features_df, user_features['total_spent']], axis=1)
user_features_final.to_csv('user_features.csv', index=False)

8. 总结

特征工程是机器学习流程中最具创造力和影响力的环节之一,它需要深入理解数据、领域知识和模型特性。通过合理的特征选择、提取、转换和创建,可以显著提升模型的性能和可解释性。

在实际应用中,特征工程是一个迭代的过程,需要不断尝试、评估和优化。随着自动化特征工程工具的发展,我们可以更高效地进行特征工程工作,但仍然需要人类的智慧和领域知识来指导这个过程。

下一集我们将学习监督学习基础,探讨如何使用这些精心设计的特征来构建和训练监督学习模型。

« 上一篇 数据预处理 下一篇 » 监督学习基础