器→工具, 工具软件

Python特征工程工具Feature-engine

钱魏Way · · 197 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!
目录

Feature-engine简介

Feature-engine 是一个专门用于特征工程的 Python 库,旨在简化数据预处理和特征构建的流程。其设计兼容 scikit-learn 的 API(如 fit() 和 transform() 方法),支持无缝集成到机器学习管道(Pipeline)中。

核心优势

  • 兼容性
    • 完全兼容 scikit-learn 的Pipeline,支持与其他库(如 pandas、numpy)无缝协作。
    • 输出保留 DataFrame 结构(而非数组),便于跟踪特征名称。
  • 灵活性
    • 自动检测变量类型(数值型/分类型)或允许手动指定。
    • 支持针对不同变量类型应用不同转换(如数值变量标准化,分类变量独热编码)。
  • 高效性
    • 提供现成的转换器,减少重复代码。
    • 支持批量处理多个特征,提升代码简洁性。

对比其他工具

  • vs scikit-learn:Feature-engine 提供更多专用转换器(如 RareLabelEncoder),而 scikit-learn 更通用但功能有限。
  • vs featuretools:Featuretools 专注于自动化特征生成(如深度特征合成),而 Feature-engine 更侧重数据清洗和传统特征工程。
  • vs category_encoders:Feature-engine 功能更全面,涵盖缺失值处理、特征创建等模块。

Feature-engine 提供丰富的特征工程工具,涵盖以下核心领域:

缺失值处理

Feature-engine 提供了一套强大且灵活的缺失值处理工具,支持多种填充策略,并能与机器学习管道无缝集成。

核心缺失值处理类

Feature-engine 的缺失值处理模块位于 feature_engine.imputation 包中,包含以下核心类:

ArbitraryNumberImputer

功能:用自定义数值填充缺失(如 -999、0)。

适用场景:数值型变量,需保留缺失信息作为特殊标记。

参数:

ArbitraryNumberImputer(
    arbitrary_number=0,  # 填充值
    variables=None       # 指定处理的列(默认自动检测数值变量)
)

MeanMedianImputer

功能:用均值或中位数填充。

适用场景:数值型变量,缺失机制为随机缺失(MCAR)。

参数:

MeanMedianImputer(
    imputation_method='mean',  # 'mean'或'median'
    variables=None
)

EndTailImputer

功能:用分布的尾部值(如分位数)填充。

适用场景:数值型变量,假设缺失值代表极端情况。

参数:

EndTailImputer(
    imputation_method='gaussian',  # 'gaussian'(均值±3σ)或'iqr'(Q1-1.5IQR / Q3+1.5IQR)
    tail='right',                 # 使用左尾或右尾值
    variables=None
)

CategoricalImputer

功能:处理分类变量缺失,支持高频类别或自定义值填充。

参数:

CategoricalImputer(
    imputation_method='missing',  # 'missing'(填充"Missing")、'frequent'(高频类别)或自定义值
    fill_value='Unknown',
    variables=None
)

RandomSampleImputer

功能:从非缺失值中随机采样填充。

适用场景:数值或分类变量,保持原始数据分布。

参数:

RandomSampleImputer(
    random_state=42,      # 随机种子
    seeding_method='add', # 随机策略('add'或'replace')
    variables=None
)

AddMissingIndicator

功能:为缺失值添加二元指示变量(0/1)。

适用场景:捕捉缺失模式,增强模型对缺失的敏感性。

参数:

AddMissingIndicator(
    missing_only=True,   # 仅对有缺失的列生成指示变量
    suffix='_na',        # 指示变量后缀
    variables=None
)

DropMissingData

按列缺失率删除特征

场景:删除缺失值比例超过阈值的列(如缺失率>30%的变量)。

参数:

from feature_engine.imputation import DropMissingData

# 删除缺失率超过30%的列
imputer = DropMissingData(
    variables=None,          # 自动检测所有列
    missing_only=True,       # 仅处理实际含缺失的列
    threshold=0.3,           # 列缺失率阈值(0.3即30%)
    axis=1                   # 按列删除(axis=0为删除行)
)

按行缺失值删除样本

场景:删除含缺失值的行(如数据清洗阶段)。

imputer = DropMissingData(
    variables=["age", "income"],  # 仅检查指定列
    threshold=0.01,               # 行中缺失值占比>1%则删除
    axis=0                        # 删除行
)

实际应用示例

场景:混合数据类型(数值型+分类型)的缺失处理

import pandas as pd
from feature_engine.imputation import (
    AddMissingIndicator,
    MeanMedianImputer,
    CategoricalImputer
)

# 示例数据
data = pd.DataFrame({
    "age": [25, 30, None, 40, None],
    "income": [50000, None, 70000, None, 60000],
    "color": ["red", "blue", None, "green", None]
})

# 1. 添加缺失指示变量
indicator = AddMissingIndicator(suffix="_missing")
data = indicator.fit_transform(data)

# 2. 填充数值变量(中位数)
num_imputer = MeanMedianImputer(imputation_method="median")
data = num_imputer.fit_transform(data)

# 3. 填充分类变量("Missing")
cat_imputer = CategoricalImputer(imputation_method="missing")
data = cat_imputer.fit_transform(data)

print(data)

高级处理策略

组合填充与指示变量

同时填充缺失并保留缺失位置信息:

from sklearn.pipeline import Pipeline

pipeline = Pipeline([
    ('indicator', AddMissingIndicator()),
    ('num_imputer', MeanMedianImputer()),
    ('cat_imputer', CategoricalImputer())
])

分类型变量自动化处理

自动检测并处理所有分类型变量:

cat_imputer = CategoricalImputer(variables=None)  # 自动检测object/category类型

时间序列缺失处理

对时间序列中的缺失值进行前后填充:

from feature_engine.imputation import DropMissingData

# 删除时间序列中连续缺失的片段
imputer = DropMissingData(
    variables=["temperature"],
    missing_only=True,
    threshold=0.5  # 若某行缺失超过50%则删除
)

最佳实践与注意事项

  • 数据泄露预防
    • 在交叉验证中,确保fit() 仅使用训练数据(如通过 Pipeline)。
    • 避免在fit() 中使用全数据计算统计量(如均值)。
  • 混合策略选择
    • 对关键特征使用AddMissingIndicator,普通特征直接填充。
    • 数值变量优先用EndTailImputer 处理异常缺失。
  • 性能优化
    • 大数据集使用RandomSampleImputer 时,设置 seeding_method=’add’ 加速采样。
    • 使用variables 参数显式指定列,避免自动检测开销。
  • 与 scikit-learn 对比
    • 优势:Feature-engine 支持更多填充策略,且保留 DataFrame 结构。

示例对比:

# scikit-learn 需自定义分类填充
from sklearn.impute import SimpleImputer
num_imputer = SimpleImputer(strategy="median")
cat_imputer = SimpleImputer(strategy="most_frequent")

适用场景总结

方法 适用场景 优点
MeanMedianImputer 数值变量,缺失随机且分布对称 简单快速,保持数据集中趋势
RandomSampleImputer 数值/分类变量,需保持原始分布 避免引入偏差,适合非随机缺失
AddMissingIndicator 缺失可能包含业务信息(如用户未填写收入) 增强模型对缺失模式的识别能力
EndTailImputer 假设缺失值代表极端情况(如超高收入) 保留数据分布尾部特征

完整流程示例(管道集成)

from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from feature_engine.imputation import (
    AddMissingIndicator,
    MeanMedianImputer,
    CategoricalImputer
)

pipeline = Pipeline([
    # 缺失处理
    ('missing_indicator', AddMissingIndicator()),
    ('impute_num', MeanMedianImputer()),
    ('impute_cat', CategoricalImputer()),

    # 后续步骤(如编码、特征选择等)
    ('classifier', RandomForestClassifier())
])

# 训练与预测
pipeline.fit(X_train, y_train)
pipeline.predict(X_test)

变量转换

Feature-engine 的变量转换功能覆盖了数值型和分类型变量的多种预处理需求,旨在提升数据分布、适应模型假设或增强特征表达。

数值变量转换

数学变换

LogTransformer(对数变换)

原理:对变量取自然对数: $X’ = \ln(X + c)$ ,其中 c 为可选常数,避免对零或负值取对数。

场景:

  • 右偏分布修正:收入、房价等右尾较长的数据
  • 方差稳定:解决异方差问题(
  • 回归残差分析)

参数:

from feature_engine.transformation import LogTransformer

transformer = LogTransformer(
    variables=["income", "price"],  # 指定列
    base='e',                      # 对数底数(可选'e'或10)
    c=1e-5                         # 加常数避免零值(默认0)
)

注意:

  • 输入数据需满足X + c > 0
  • 对含零的数据建议设置c=1 或 c=0.5

LogCpTransformer(对数偏移变换)

原理:自动计算最佳偏移常数 c ,使 X + c > 0 ,再取对数: $X’ = \ln(X + c)$ 。

场景:

  • 数据含零或轻微负值,需自动确定最小偏移量
  • 简化对数变换的参数调优

参数:

from feature_engine.transformation import LogCpTransformer

transformer = LogCpTransformer(
    variables=["sales"],
    c_method='auto'  # 自动计算最小c(也可手动指定c=0.1)
)

若数据最小值为 -5,则自动设置 c = 5.1 保证 X + c > 0

ReciprocalTransformer(倒数变换)

原理:取倒数: $X’ = \frac{1}{X}$

场景:

  • 变量与目标呈反比例关系(如物理中的电阻与电流)
  • 修正左偏分布

参数:

from feature_engine.transformation import ReciprocalTransformer

transformer = ReciprocalTransformer(
    variables=["density"],
    denom=1e-6  # 分母加小常数避免除以零(默认1e-6)
)

注意:

  • 当X 接近零时结果会剧烈波动,需谨慎使用

ArcsinTransformer(反正弦变换)

原理:计算反正弦平方根: $X’ = \arcsin(\sqrt{X})$

场景:

  • 比例数据(取值范围[0,1]),如点击率、转化率
  • 生物统计中的比例数据正态化

参数:

from feature_engine.transformation import ArcsinTransformer

transformer = ArcsinTransformer(
    variables=["conversion_rate"]
)

注意:

  • 仅适用于$X \in [0, 1]$ ,超范围值会报错

PowerTransformer(幂变换)

原理:应用幂函数: $X’ = X^k$ ,其中 k 为指数参数。

场景:

  • 自定义指数调整分布形态
  • 线性模型的特征非线性扩展

参数:

from feature_engine.transformation import PowerTransformer

transformer = PowerTransformer(
    variables=["population"],
    exp=0.5    # 开平方(k=0.5)
)

常用指数:

  • k=2: 平方(修正左偏)
  • k=0.5: 平方根(修正右偏)

BoxCoxTransformer(Box-Cox变换)

原理:参数化幂变换:$X’ = \begin{cases} \frac{X^\lambda – 1}{\lambda} & \text{if } \lambda \neq 0 \\ \ln(X) & \text{if } \lambda = 0 \end{cases}$

场景:

  • 自动寻找最优\lambda 使数据接近正态分布
  • 处理正数右偏数据(要求X > 0 )

参数:

from feature_engine.transformation import BoxCoxTransformer

transformer = BoxCoxTransformer(
    variables=["income"],
    param_method='mle'  # 最大似然法估计lambda(默认)
)

输出属性

  • lambda_dict_: 各变量的最优λ值

YeoJohnsonTransformer(Yeo-Johnson变换)

原理:扩展Box-Cox至全实数域:$X’ = \begin{cases} \frac{(X+1)^\lambda – 1}{\lambda} & \text{if } X \geq 0, \lambda \neq 0 \\ \ln(X+1) & \text{if } X \geq 0, \lambda = 0 \\ \frac{(-X+1)^{2-\lambda} – 1}{2-\lambda} & \text{if } X < 0, \lambda \neq 2 \\ -\ln(-X+1) & \text{if } X < 0, \lambda = 2 \end{cases}$

场景:

  • 数据包含零或负值时的正态化转换
  • 比Box-Cox更通用的分布修正

参数:

from feature_engine.transformation import YeoJohnsonTransformer

transformer = YeoJohnsonTransformer(
    variables=["temperature"],
    param_method='mle'
)

分箱(离散化)

EqualFrequencyDiscretiser(等频分箱)

原理:将数据划分为 k 个箱,每个箱的样本数量大致相同。

  • 公式:若总样本数为n ,则每箱样本数 ≈ n/k
  • 边界确定:按分位数划分,如3箱对应3%和66.6%分位数

适用场景:

  • 数据分布严重倾斜(如收入、点击量)
  • 需平衡各分箱样本量以支持分类模型训练

参数配置:

from feature_engine.discretisation import EqualFrequencyDiscretiser

disc = EqualFrequencyDiscretiser(
    q=5,                # 分箱数(或直接写n_bins=5)
    variables=["age", "income"],
    return_object=True,  # 返回类别型(否则返回数值编码)
    return_boundaries=False  # 是否在变量名后附加边界信息
)

注意事项:

  • 数据存在大量重复值时,实际分箱数可能小于k
  • 分箱边界可能因数据微小变化而波动(对稳定性要求高的场景慎用)

EqualWidthDiscretiser(等宽分箱)

原理:将数据值域均匀划分为 k 个等宽区间。

  • 公式:箱宽$w = \frac{\text{max}(X) – \text{min}(X)}{k}$
  • 边界:$\text{min}(X) + i \times w ( i=1,2,…,k-1 )$

适用场景:

  • 数据分布均匀,无显著异常值
  • 需直观理解区间范围(如温度分箱为低温、中温、高温)

参数配置:

from feature_engine.discretisation import EqualWidthDiscretiser

disc = EqualWidthDiscretiser(
    bins=5,              # 分箱数
    variables=["price"],
    return_object=True
)

注意事项:

  • 对异常值敏感(极端值可能导致某些箱样本极少)
  • 需结合业务理解判断宽度合理性(如年龄分箱是否应以10年为间隔)

GeometricWidthDiscretiser(几何宽度分箱)

原理:按几何级数(指数增长)划分区间,箱宽随数值增大而增加。

  • 公式:箱边界为$a \times r^i$ ,其中 r 为公比, i 为分箱索引

适用场景:数值范围跨越多个数量级(如收入、城市人口)

参数配置:

from feature_engine.discretisation import GeometricWidthDiscretiser

disc = GeometricWidthDiscretiser(
    variables=["population"],
    start=1000,          # 起始边界
    r=10,                # 公比(每个箱宽度是前一个的r倍)
    precision=2          # 边界小数精度
)

注意事项:

  • 起始值 (start) 需小于数据最小值,否则首箱可能为空
  • 适合处理右偏分布,但需避免公比过大导致后几个箱过宽

DecisionTreeDiscretiser(决策树分箱)

原理:利用决策树(或随机森林)的分裂点作为分箱边界,最大化目标变量区分度。

步骤:

  • 训练回归树或分类树拟合目标变量
  • 提取树节点的分裂阈值作为分箱边界

适用场景:

  • 有监督分箱,捕捉变量与目标的非线性关系
  • 需要高信息增益的分箱策略(如信用评分模型)

参数配置:

from feature_engine.discretisation import DecisionTreeDiscretiser
from sklearn.tree import DecisionTreeRegressor

disc = DecisionTreeDiscretiser(
    cv=3,              # 交叉验证折数
    scoring='neg_mean_squared_error',  # 回归任务评估指标
    variables=["age"],
    regression=True,   # 是否为回归任务(False为分类)
    estimator=DecisionTreeRegressor(max_depth=3),
    param_grid={'max_depth': [2, 3]},  # 网格搜索调参
)

注意事项:

  • 树模型可能过拟合,需通过max_depth 控制复杂度
  • 分箱结果可能不稳定(尤其在小数据集上),建议增加交叉验证

ArbitraryDiscreriser(自定义分箱)

原理:用户手动指定分箱边界,灵活适应业务规则或领域知识。

场景:

  • 年龄分段(如18-25岁为“青年”)
  • 根据行业标准划分风险等级

参数配置:

from feature_engine.discretisation import ArbitraryDiscretiser

disc = ArbitraryDiscretiser(
    binning_dict={
        "age": [18, 30, 50],       # 边界点(生成区间:<18, 18-30, 30-50, >50)
        "income": [3000, 10000]
    },
    return_object=True
)

注意事项:

  • 需确保边界覆盖所有数据范围,否则超出部分归为(-inf, min)或(max, inf)
  • 分箱名称可通过return_boundaries=False 转为整数编码

分箱方法对比与选型指南

方法 监督/非监督 适用场景 优点 缺点
EqualFrequency 非监督 数据倾斜严重 平衡样本分布 对数据微小变化敏感
EqualWidth 非监督 分布均匀、无异常值 直观易懂 受异常值影响大
GeometricWidth 非监督 数值跨度大(如收入、人口) 适应指数分布 需手动设置起始值和公比
DecisionTree 监督 变量与目标非线性相关 最大化信息增益 计算成本高,可能过拟合
Arbitrary 非监督 业务规则驱动 高度灵活 依赖领域知识

缩放与标准化

Feature-engine 的 MeanNormalizationScaler 是一种数据标准化方法,将数据缩放到指定范围,同时使均值为零。它结合了中心化和缩放的双重作用,适用于需要数据以零为中心且限制范围的场景(如神经网络、距离度量模型)。

计算公式:

对每个特征独立进行以下变换:

$$X’ = \frac{X – \mu}{X_{\text{max}} – X_{\text{min}}} \times \text{scale} + \text{center}$$

  • $\mu$: 特征的均值
  • $X_{\text{max}}, X_{\text{min}}$: 特征的最大值和最小值
  • scale: 缩放后的范围半径(默认5)
  • center: 缩放后的中心点(默认0)

默认行为(scale=0.5, center=0):将数据映射到 [-0.5, 0.5] 区间,均值为0。

参数详解:

from feature_engine.transformation import MeanNormalizationScaler

scaler = MeanNormalizationScaler(
    variables=["age", "income"],  # 指定要标准化的列(None则自动选数值型变量)
    scale=0.5,                    # 缩放后的范围半径(默认数据范围的一半)
    center=0,                     # 缩放后的中心点(默认0)
    clip=False                     # 是否将超出范围的值截断到边界(默认False)
)

分类变量编码

Feature-engine 提供多种分类变量编码方法,用于将类别型特征转化为数值形式以适配机器学习模型。

OneHotEncoder(独热编码)

原理:将每个类别转化为二元特征(0/1),生成 k 列( k 为类别数)。

  • 示例:颜色(红、蓝、绿)→ 3列:color_red,color_blue, color_green

适用场景:

  • 无序类别(如城市、产品类型)
  • 类别数较少(一般 <15,避免维度爆炸)

参数配置:

from feature_engine.encoding import OneHotEncoder

encoder = OneHotEncoder(
    variables=["color", "city"],  # 指定列(默认处理所有类别型变量)
    drop_last=True,               # 是否删除最后一列(避免多重共线性)
    drop_first=False,             # 是否删除第一列(与drop_last互斥)
    ignore_format=False           # 是否自动检测类别型变量(默认False需手动指定)
)

注意事项:

  • 高基数类别(如用户ID)会导致特征维度激增,需先做分箱或过滤
  • 线性模型需配合drop_first=True避免共线性问题

OrdinalEncoder(序数编码)

原理:将类别按自定义或频率顺序映射为有序整数(如1,2,3)。

  • 示例:学历(高中、本科、硕士)→ [1, 2, 3]

适用场景

  • 有序类别(如评分等级、学历)
  • 树模型需要保留类别顺序信息

参数配置:

from feature_engine.encoding import OrdinalEncoder

encoder = OrdinalEncoder(
    encoding_method="ordered",     # 可选"ordered"(按目标均值排序)或"arbitrary"(按出现顺序)
    variables=["education"],
    ignore_format=False
)

自定义顺序:

encoder = OrdinalEncoder(
    encoding_method="arbitrary",
    variables=["education"],
    user_dict={"education": {"高中": 1, "本科": 2, "硕士": 3}}  # 手动指定映射
)

CountFrequencyEncoder(计数/频率编码)

原理:用类别出现次数或频率代替原始类别。

  • 计数编码:$X’ = \text{count}(X)$
  • 频率编码:$X’ = \frac{\text{count}(X)}{N}$

适用场景:

  • 高基数类别(如邮政编码、用户ID)
  • 类别出现频率与目标相关(如地区用户活跃度)

参数配置:

from feature_engine.encoding import CountFrequencyEncoder

encoder = CountFrequencyEncoder(
    encoding_method="count",  # 可选"count"或"frequency"
    variables=["zipcode"]
)

MeanEncoder(目标均值编码)

原理

用目标变量均值替代类别(回归任务)或类别概率(分类任务)。

  • 回归:$X’ = \text{mean}(y | X)$
  • 分类:$X’ = P(y=1 | X)$

适用场景:

  • 高基数类别且与目标相关(如用户ID与购买概率)
  • 树模型、线性模型的特征增强

参数配置:

from feature_engine.encoding import MeanEncoder

encoder = MeanEncoder(
    variables=["user_id"],
    ignore_format=False,
    smooth=0.1  # 平滑系数(防止过拟合)
)

WoEEncoder(权重证据编码)

原理:计算Weight of Evidence (WoE),衡量类别对目标事件的预测能力:

$$\text{WoE} = \ln\left( \frac{P(X|y=1)}{P(X|y=0)} \right)$$

适用场景:信用评分、二分类任务(如客户流失预测)

参数配置:

from feature_engine.encoding import WoEEncoder

encoder = WoEEncoder(
    variables=["loan_type"],
    ignore_format=False
)

RareLabelEncoder(稀有类别编码)

原理:将低频类别合并为“Rare”或其他指定标签,减少噪声。

合并策略:

  • 频率低于阈值(如5%)
  • 出现次数少于N次

适用场景:处理长尾分布中的稀有类别(如罕见疾病诊断码)

参数配置:

from feature_engine.encoding import RareLabelEncoder

encoder = RareLabelEncoder(
    tol=0.05,          # 频率阈值(默认5%)
    n_categories=10,   # 最大保留类别数(超出部分合并)
    variables=["diagnosis_code"],
    replace_with="Rare"
)

DecisionTreeEncoder(决策树编码)

原理:用决策树叶节点编号或预测概率替代类别。

步骤:

  • 训练决策树(分类或回归)
  • 将样本映射到叶节点(生成离散编码或概率)

适用场景:高基数类别且与目标存在复杂非线性关系

参数配置:

from feature_engine.encoding import DecisionTreeEncoder
from sklearn.tree import DecisionTreeClassifier

encoder = DecisionTreeEncoder(
    estimator=DecisionTreeClassifier(max_depth=3),
    cv=3,                # 交叉验证折数
    scoring="roc_auc",   # 评估指标
    regression=False,    # 是否为回归任务
    variables=["user_id"]
)

StringSimilarityEncoder(字符串相似性编码)

原理:将字符串转化为相似性矩阵,基于编辑距离、余弦相似度等度量。

  • 示例:公司名称 → 与其他名称的相似度得分矩阵

适用场景:非结构化文本的模糊匹配(如地址、产品名称去重)

参数配置:

from feature_engine.encoding import StringSimilarityEncoder

encoder = StringSimilarityEncoder(
    variables=["company_name"],
    similarity="cosine",  # 相似度度量(可选"levenshtein", "jaro"等)
    n_features=5          # 生成相似性特征的数量
)

编码方法对比与选择指南

编码器 输出类型 适用模型 优点 缺点
OneHotEncoder 二元向量 线性模型、神经网络 无信息损失 维度爆炸(高基数类别)
MeanEncoder 连续值 树模型、线性模型 捕捉类别-目标关系 可能过拟合
WoEEncoder 连续值 逻辑回归、评分卡 可解释性强 仅适用于二分类
CountFrequency 连续值 树模型、聚类 处理高基数类别 忽略类别与目标关系
StringSimilarity 相似性矩阵 聚类、KNN 支持模糊匹配 计算成本高
DecisionTree 离散/连续值 所有模型 捕捉复杂非线性关系 训练时间长

最佳实践与总结

选择策略

  • 低基数无序类别→ OneHotEncoder
  • 有序类别→ OrdinalEncoder
  • 高基数类别→ MeanEncoder 或 CountFrequencyEncoder
  • 文本模糊匹配→ StringSimilarityEncoder
  • 信用风险模型→ WoEEncoder

防止过拟合

  • 使用MeanEncoder时增加平滑(smooth参数)
  • 对稀有类别先做RareLabelEncoder合并

注意事项

  • 目标泄漏:MeanEncoder、WoEEncoder需在训练集上拟合,避免测试集信息泄露
  • 类别一致性:测试集可能出现未见类别,需用handle_unknown=”ignore”或归为稀有类别

时间序列与周期变量

时间序列数据在机器学习中具有独特挑战,需通过特征工程捕捉时间依赖、周期性及趋势。

DatetimeFeatures(日期时间特征提取)

原理:从日期时间列中提取多粒度时间成分(如年、月、日、小时、周几等),转化为结构化特征。

适用场景:

  • 捕捉周期性(如“小时”对交通流量预测重要)
  • 分解时间维度以增强模型解释性

参数配置:

from feature_engine.datetime import DatetimeFeatures

dtf = DatetimeFeatures(
    variables="datetime_column",  # 日期时间列名
    features_to_extract=["year", "month", "weekday", "hour"],  # 提取成分
    drop_original=True,            # 是否删除原始列
    utc=True                        # 是否转换时区为UTC
)

注意事项:

  • 自动处理时区(建议统一为UTC避免跨时区问题)
  • 类别型成分(如weekday)需后续编码(如OneHotEncoder)

DatetimeSubtraction(日期时间差值计算)

原理:计算两列日期时间的间隔时长,生成数值特征(如天数、秒数)。

公式: $\Delta t = t_2 – t_1$

适用场景:

  • 用户行为分析(如注册到首次购买间隔)
  • 设备故障预测(上次维护至今的时间)

参数配置:

from feature_engine.datetime import DatetimeSubtraction

dts = DatetimeSubtraction(
    variables=["start_time", "end_time"],  # 待计算的两列
    output_unit="D",                       # 输出单位(D=天,h=小时,s=秒)
    new_column_name="duration"             # 新特征列名
)

注意事项:

  • 确保两列均为datetime类型
  • 差值可能为负(需处理逻辑冲突,如abs=True参数)

LagFeatures(滞后特征)

原理:将历史值作为当前时刻的特征,捕捉时间依赖性。

公式: $X_{t-k}$ (k为滞后步数)

适用场景:

  • 时序预测(如用前3天销售额预测当天)
  • 事件影响分析(广告曝光后第n天的转化率)

参数配置:

from feature_engine.timeseries.forecasting import LagFeatures

lag = LagFeatures(
    variables=["sales", "temperature"],  # 需要滞后的列
    freq=["1D", "2D"],                   # 滞后步长(1天前、2天前)
    drop_original=False                  # 是否保留原始值
)

注意事项:

  • 滞后导致前k行出现NaN,需填充或删除
  • 避免在测试集中使用未来信息(需严格按时间划分训练/测试)

WindowFeatures(滚动窗口特征)

原理:计算指定窗口内的统计量(均值、标准差等),反映短期趋势。

公式: $\text{mean}(X_{t-w+1:t})$ ,其中w为窗口大小

适用场景:

  • 平滑噪声(如7日移动平均)
  • 检测近期波动(如近3小时最大值)

参数配置:

from feature_engine.timeseries.forecasting import WindowFeatures

window = WindowFeatures(
    variables=["sales"], 
    window="3D",          # 窗口大小(3天)
    functions=["mean", "max"],  # 统计函数
    freq="1D",           # 窗口滑动步长(默认1天)
    drop_original=False
)

注意事项:

  • 窗口统计需对齐时间索引(建议设置DatetimeIndex)
  • 初始窗口不足时结果为NaN,可设置min_periods参数指定最小观测数

ExpandingWindowFeatures(扩展窗口特征)

原理:计算从序列起点到当前时刻的累积统计量,反映长期趋势。

公式: $\text{sum}(X_{1:t})$

适用场景:

  • 累积计算(如年度累计销售额)
  • 基线对比(当前值 vs 历史平均)

参数配置:

from feature_engine.timeseries.forecasting import ExpandingWindowFeatures

expanding = ExpandingWindowFeatures(
    variables=["sales"],
    functions=["cumsum", "cummax"],  # 累积函数
    min_periods=1,                   # 最小观测数(默认1)
    drop_original=False
)

注意事项:

  • 扩展窗口对内存需求较高(长时序需谨慎)
  • 累积值可能随时间增长超出模型敏感范围,需标准化

方法对比与选型指南

方法 输出类型 捕捉模式 典型应用
DatetimeFeatures 多列类别/数值 周期性、季节性 节假日效应分析
DatetimeSubtraction 数值 事件间隔时长 用户生命周期阶段划分
LagFeatures 数值 短期依赖 自回归预测模型(AR)
WindowFeatures 数值 近期趋势 移动平均线(MA)
ExpandingWindow 数值 长期累积效应 累计收益计算

自定义转换

FunctionTransformer:应用自定义函数

from feature_engine.transformation import FunctionTransformer

# 示例:对年龄取平方
transformer = FunctionTransformer(
    variables=["age"],
    func=lambda x: x**2,
    inverse_func=None  # 可定义逆变换(可选)
)

最佳实践与技巧

自动检测变量类型

大多数转换器支持 variables=None,自动检测数值型或分类型变量:

# 自动检测所有数值变量进行对数变换
log_transformer = LogTransformer(variables=None)

管道集成

将转换步骤整合到 Scikit-learn 管道:

from sklearn.pipeline import Pipeline

pipeline = Pipeline([
    ('log', LogTransformer(variables=["income"])),
    ('onehot', OneHotEncoder()),
    ('scaler', StandardScaler()),
    ('model', LogisticRegression())
])

处理未知类别

使用RareLabelEncoder或设置unseen=’ignore’避免测试集出现新类别:

encoder = OneHotEncoder(handle_unknown='ignore')

分箱后保留原始值

使用 return_object=True 将分箱结果转为字符串类型:

binner = EqualWidthDiscretiser(return_object=True)

注意事项

  • 数据泄露:始终在训练集上fit(),再应用到测试集 transform()
  • 稀疏矩阵:独热编码后使用sparse 节省内存
  • 非线性模型:树模型(如随机森林)通常不需要变量转换,优先处理分类型变量

特征创建

Feature-engine 的特征创建功能专注于通过数学组合、时间序列操作和领域知识生成新特征,极大简化了特征工程的复杂度。

MathFeatures(数学组合特征)

原理:通过数学运算(加、减、乘、除等)组合多个特征,生成新特征以捕捉交互作用或非线性关系。

适用场景:

  • 业务逻辑驱动:如收入 = 单价 × 销量
  • 特征交互:如温度 × 湿度 影响体感温度
  • 多项式扩展:生成平方项、交叉项(需配合PolynomialFeatures)

参数配置:

from feature_engine.creation import MathFeatures

transformer = MathFeatures(
    variables=["price", "cost"],  # 参与运算的特征列
    math_operations=["sum", "prod", "div"],  # 运算类型(sum, diff, prod, div)
    new_variables_names=["total", "profit_margin", "price_cost_ratio"]  # 新列名(需与运算顺序对应)
)

注意事项:

  • 特征缩放:乘除运算可能导致数值范围差异大,需标准化
  • 多重共线性:避免生成过多相关特征(如同时包含x+y 和 x×y)
  • 零除错误:确保分母非零(可通过missing_values=”ignore” 处理)

RelativeFeatures(相对特征)

原理:计算特征相对于某个基准的比率或差值,揭示相对关系。

适用场景:

  • 基准对比:如当前温度 vs 月平均温度
  • 比例分析:如部门预算占总预算比例
  • 归一化变体:(x – min) / (max – min)的灵活实现

参数配置:

from feature_engine.creation import RelativeFeatures

transformer = RelativeFeatures(
    reference=["budget_total"],  # 基准特征列
    variables=["budget_marketing", "budget_rd"],  # 待比较特征列
    func=["ratio", "diff"]        # 计算方式(ratio=比值, diff=差值)
)

注意事项:

  • 基准选择:需业务理解(如总销售额 vs 行业平均)
  • 动态基准:若基准随时间变化(如滚动平均),需结合时序方法

CyclicalFeatures(周期性特征编码)

原理:将周期性特征(如小时、月份)转换为正弦/余弦形式,使模型理解周期性规律。

适用场景:

  • 时间周期:小时、星期、月份、季度
  • 方向性周期:如风向(0°与360°等价)

参数配置:

from feature_engine.creation import CyclicalFeatures

transformer = CyclicalFeatures(
    variables=["hour"],  # 待编码的周期特征
    max_values={"hour": 24},  # 周期最大值(如小时为24)
    drop_original=True  # 是否删除原始列
)

数学原理:

  • $x_{\text{sin}} = \sin\left( \frac{2\pi x}{max} \right)$
  • $x_{\text{cos}} = \cos\left( \frac{2\pi x}{max} \right)$

注意事项:

  • 非周期特征:若数值无周期性(如年龄),无需此转换
  • 多周期:同时处理多个周期(如小时+月份)需分别指定max_values

DecisionTreeFeatures(决策树特征)

原理:利用决策树模型的叶节点索引或预测值作为新特征,捕捉复杂非线性关系。

适用场景:

  • 高维非线性:特征交互复杂且难以手动构造
  • 模型融合:作为元特征输入到线性模型或神经网络

参数配置:

from feature_engine.creation import DecisionTreeFeatures
from sklearn.tree import DecisionTreeRegressor

transformer = DecisionTreeFeatures(
    estimator=DecisionTreeRegressor(max_depth=3),  # 自定义决策树
    cv=3,                    # 交叉验证折数(防止过拟合)
    regression=True,         # 是否为回归任务
    variables=["age", "income"]  # 参与训练的特征
)

生成特征类型:

  • 叶节点索引:离散特征(适合树模型、One-Hot编码)
  • 预测概率/值:连续特征(适合线性模型)

注意事项:

  • 过拟合风险:树深度过大可能导致训练集过拟合
  • 特征重要性:可通过feature_importances_ 分析贡献度

方法对比与选型指南

方法 输出类型 核心作用 典型用例
MathFeatures 数值 特征交互/组合 物理公式、业务指标计算
RelativeFeatures 数值 基准对比/比例分析 市场份额、增长率计算
CyclicalFeatures 数值 周期性编码 时间特征、角度数据
DecisionTreeFeatures 离散/连续 非线性关系提取 复杂模式的特征增强

异常值处理

异常值处理是数据预处理的关键步骤,能有效提升模型鲁棒性。

Winsorizer(缩尾处理)

原理:将超出指定分位数的值替换为分位数值,保留数据分布主体。

公式:

  • 左尾:将低于$q_{\text{low}}$ 的值设为$ q_{\text{low}}$
  • 右尾:将高于$q_{\text{high}}$ 的值设为 $q_{\text{high}}$

适用场景:

  • 数据分布存在长尾(如收入、房价)
  • 需保留数据规模,避免删除样本

参数配置:

from feature_engine.outliers import Winsorizer

winsorizer = Winsorizer(
    capping_method="quantiles",   # 可选"quantiles"或"gaussian"(基于标准差)
    tail="both",                   # 处理方向("left", "right", "both")
    fold=0.05,                     # 分位数范围:q_low=0.05, q_high=0.95
    variables=["income", "age"]    # 指定处理列
)

注意事项:

  • 分位数计算:对小数据集敏感,可能需调整fold
  • 多特征处理:不同列可设置不同分位数(通过variables 参数分别指定)

ArbitraryOutlierCapper(固定阈值截断)

原理:根据用户定义的上下限替换异常值,需依赖先验知识。

公式:

  • 若$x < \text{min}$ ,则$ x = \text{min}$
  • 若$x > \text{max}$ ,则 $x = \text{max}$

适用场景:

  • 明确知道特征合理范围(如年龄 0-150,温度 -50~60℃)
  • 业务规则驱动的数据清洗

参数配置:

from feature_engine.outliers import ArbitraryOutlierCapper

capper = ArbitraryOutlierCapper(
    max_capping_dict={"income": 100000, "age": 100},  # 各列上限
    min_capping_dict={"income": 1000, "age": 0}        # 各列下限
)

注意事项:

  • 阈值合理性:需结合业务背景设定,避免过度截断
  • 缺失值处理:默认不处理缺失值,需提前处理

OutlierTrimmer(异常值删除)

原理:直接删除包含异常值的行,适用于数据量大且异常值占比低的场景。

适用场景:

  • 异常值明显错误(如身高3米、年龄-10岁)
  • 数据充足,删除后不影响模型训练

参数配置:

from feature_engine.outliers import OutlierTrimmer

trimmer = OutlierTrimmer(
    capping_method="quantiles",     # 可选"quantiles"或"gaussian"
    tail="both",                    # 处理方向
    fold=0.05,                      # 分位数范围
    variables=["income", "age"]
)

注意事项:

  • 数据量减少:可能导致训练数据不足,需检查删除比例
  • 不可逆操作:删除后无法恢复,建议备份原始数据

方法对比与选型指南

方法 处理方式 优点 缺点 适用场景
Winsorizer 替换 保留数据规模,减少信息丢失 对分位数敏感,可能引入偏差 长尾分布,需保留样本
ArbitraryOutlierCapper 替换 明确可控,符合业务规则 依赖先验知识,阈值设定困难 已知合理范围(如年龄)
OutlierTrimmer 删除 彻底移除异常,简化后续分析 数据量减少,可能丢失信息 异常明显错误且数据量充足

最佳实践与注意事项

  • 优先尝试替换:在数据量较小时,尽量用Winsorizer 或 ArbitraryOutlierCapper 避免样本损失。
  • 多方法组合:对同一数据集不同列采用不同方法(如收入用Winsorizer,年龄用 Arbitrary)。
  • 可视化验证:处理前后绘制箱线图或分布图,确认效果。
  • 参数调优
    • Winsorizer的 fold 可尝试01~0.1
    • OutlierTrimmer的 fold 需根据异常值实际占比调整

完整代码示例

import matplotlib.pyplot as plt

# 数据准备
data = pd.DataFrame({
    "income": np.random.gamma(shape=2, scale=10000, size=1000),  # 右偏分布
    "temperature": np.random.normal(25, 10, 1000)               # 含负值异常
})

# 处理流程
winsorizer = Winsorizer(variables=["income"], tail="right", fold=0.05)
capper = ArbitraryOutlierCapper(
    max_capping_dict={"temperature": 50}, 
    min_capping_dict={"temperature": -10}
)

data["income"] = winsorizer.fit_transform(data[["income"]])
data["temperature"] = capper.fit_transform(data[["temperature"]])

# 可视化对比
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
data["income"].hist(ax=axes[0, 0])
data["temperature"].hist(ax=axes[1, 0])
plt.show()

高级技巧

动态阈值计算:结合 TimeSeriesSplit 在时序数据中滚动计算分位数。

自动化管道:将异常值处理嵌入 Pipeline,避免数据泄漏。

from sklearn.pipeline import Pipeline
from sklearn.linear_model import LinearRegression

pipeline = Pipeline([
    ('winsorizer', Winsorizer(tail="both", fold=0.05)),
    ('model', LinearRegression())
])

自定义检测器:通过 FunctionTransformer 实现业务特定的异常逻辑。

通过合理选择异常值处理方法,可显著提升数据质量,增强模型泛化能力。

特征选择

特征选择是优化模型性能、降低过拟合风险和提高效率的关键步骤。

基础过滤方法

DropFeatures(直接删除指定列)

原理:根据列名直接删除无关特征(如ID列、文本列)。

适用场景:已知某些列无分析价值(如用户ID、冗余描述字段)。

参数配置:

from feature_engine.selection import DropFeatures
selector = DropFeatures(features_to_drop=["id", "description"])

DropConstantFeatures(删除常量特征)

原理:删除方差为0的列(所有值相同)。

适用场景:数据中存在无效填充(如全为”Unknown”的列)。

参数配置:

from feature_engine.selection import DropConstantFeatures
selector = DropConstantFeatures(tol=1, variables=None)  # tol=允许不同值的数量

DropDuplicateFeatures(删除重复特征)

原理:删除完全相同的列(基于所有行值匹配)。

适用场景:数据采集或合并导致的重复列。

参数配置:

from feature_engine.selection import DropDuplicateFeatures
selector = DropDuplicateFeatures()

基于相关性的方法

DropCorrelatedFeatures(删除高相关特征)

原理:若两列相关性高于阈值,随机删除其一。

适用场景:多重共线性问题(如线性回归模型)。

参数配置:

from feature_engine.selection import DropCorrelatedFeatures
selector = DropCorrelatedFeatures(
    threshold=0.8,         # 相关系数阈值
    method="pearson",      # 可选"spearman"或"kendall"
    selection_method="variance"  # 保留方差更大的列
)

SmartCorrelationSelection(智能相关性选择)

原理:在相关特征组中,保留与目标变量相关性最高的一列。

适用场景:需结合目标变量筛选相关特征。

参数配置:

from feature_engine.selection import SmartCorrelationSelection
selector = SmartCorrelationSelection(
    threshold=0.8, 
    method="pearson",
    selection_method="model_performance",  # 使用模型评分选择最佳特征
    estimator=LinearRegression()
)

基于模型性能的方法

SelectBySingleFeaturePerformance(单特征模型筛选)

原理:用单特征训练模型,保留高于性能阈值的特征。

适用场景:初步筛选强预测性特征。

参数配置:

from feature_engine.selection import SelectBySingleFeaturePerformance
selector = SelectBySingleFeaturePerformance(
    estimator=LogisticRegression(), 
    scoring="roc_auc", 
    cv=3, 
    threshold=0.5
)

RecursiveFeatureElimination(递归特征消除,RFE)

原理:迭代移除对模型贡献最小的特征。

适用场景:高维数据(如基因表达数据)。

参数配置:

from feature_engine.selection import RecursiveFeatureElimination
selector = RecursiveFeatureElimination(
    estimator=RandomForestClassifier(), 
    scoring="accuracy", 
    cv=3, 
    n_features_to_select=10
)

RecursiveFeatureAddition(递归特征添加,RFA)

原理:迭代添加对模型贡献最大的特征。

适用场景:特征量极大时逐步构建特征集。

参数配置:

from feature_engine.selection import RecursiveFeatureAddition
selector = RecursiveFeatureAddition(
    estimator=LinearRegression(), 
    scoring="r2", 
    cv=3, 
    n_features_to_select=5
)

基于目标关系的方法

SelectByTargetMeanPerformance(目标均值分桶筛选)

原理:将特征分箱后计算每箱的目标均值,保留与目标关联强的特征。

适用场景:分类问题中探索特征与目标的单调关系。

参数配置:

from feature_engine.selection import SelectByTargetMeanPerformance
selector = SelectByTargetMeanPerformance(
    bins=5,                # 分箱数
    strategy="equal_freq", # 分箱方式
    scoring="roc_auc",     # 评估指标
    threshold=0.6
)

SelectByInformationValue(信息值筛选)

原理:计算IV值(Information Value),衡量特征预测能力。

适用场景:金融风控评分卡模型。

参数配置:

from feature_engine.selection import SelectByInformationValue
selector = SelectByInformationValue(
    bins=5, 
    strategy="equal_freq", 
    threshold=0.3
)

稳定性与鲁棒性方法

DropHighPSIFeatures(高PSI特征删除)

原理:计算PSI(Population Stability Index),删除分布随时间/群体变化大的特征。

适用场景:模型监控中的特征稳定性维护。

参数配置:

from feature_engine.selection import DropHighPSIFeatures
selector = DropHighPSIFeatures(
    split_col="year",     # 划分群体的列(如年份)
    split_frac=0.5,       # 划分比例(如训练集与测试集)
    threshold=0.2
)

ShuffleFeaturesSelector(特征置换重要性评估)

原理:打乱特征值后计算模型性能下降幅度,评估特征重要性。

适用场景:黑盒模型的特征重要性分析。

参数配置:

from feature_engine.selection import ShuffleFeaturesSelector
selector = ShuffleFeaturesSelector(
    estimator=GradientBoostingClassifier(), 
    scoring="f1", 
    cv=3, 
    max_iter=50          # 最大迭代次数
)

高级特征选择技术

MRMR(最大相关最小冗余)

原理:选择与目标高度相关且彼此低冗余的特征。

适用场景:需平衡特征信息量与冗余度。

参数配置:

from feature_engine.selection import MRMR
selector = MRMR(
    num_features_to_select=10, 
    method="pearson",     # 相关性计算方法
    target_r="pearson"    # 目标相关性方法
)

ProbeFeatureSelection(探针特征筛选)

原理:添加随机噪声特征(探针),删除重要性低于探针的特征。

适用场景:验证特征是否显著优于随机噪声。

参数配置:

from feature_engine.selection import ProbeFeatureSelection
selector = ProbeFeatureSelection(
    estimator=RandomForestClassifier(), 
    scoring="accuracy", 
    cv=3, 
    n_probes=5            # 探针特征数量
)

参考链接:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注