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 # 探针特征数量 )
参考链接: