Featuretools 是一个强大的 Python 库,专注于自动化特征工程,特别是处理关系型数据和时间序列数据。其核心思想是通过“深度特征合成”算法,自动将多个表之间的关联关系转化为机器学习可用的特征矩阵。
下面我将通过大量代码实例,带你从零开始掌握 Featuretools。
1. 核心概念
在编写代码前,先理解三个关键组件:
EntitySet(实体集):数据集的集合,每个“实体”可以看作一个 DataFrame,实体之间通过“关系”连接(例如用户表和订单表)。
Deep Feature Synthesis (DFS):核心算法,通过“堆叠”特征基元沿着关系路径生成新特征。
Feature Primitives(特征基元):基础特征操作方法,分为聚合基元(如 sum, mean)和转换基元(如 hour, month)。
2. 基础示例:从多表数据生成特征
2.1 加载内置数据并创建 EntitySet
Featuretools 内置了一个模拟电商数据的示例,包含 customers、sessions 和 transactions 三个表。
import featuretools as ft# 加载示例数据(返回字典形式的多个DataFrame)data = ft.demo.load_mock_customer()# 查看数据结构customers_df = data["customers"]sessions_df = data["sessions"]transactions_df = data["transactions"]print("customers shape:", customers_df.shape)print("sessions shape:", sessions_df.shape)print("transactions shape:", transactions_df.shape)
输出示例:
customers shape: (5, 4)sessions shape: (35, 4)transactions shape: (500, 5)
接下来,我们将这三个 DataFrame 组织成一个 EntitySet,并定义它们之间的关系:
# 创建实体集es = ft.EntitySet(id="customer_data")# 添加各个实体,指定索引列和时间列(如果有)es = es.add_dataframe( dataframe_name="customers", dataframe=customers_df, index="customer_id",)es = es.add_dataframe( dataframe_name="sessions", dataframe=sessions_df, index="session_id", time_index="session_start", # 时间列,用于时间序列特征)es = es.add_dataframe( dataframe_name="transactions", dataframe=transactions_df, index="transaction_id", time_index="transaction_time",)# 定义表之间的关系# 关系: (父表, 父键, 子表, 子键)es = es.add_relationship("sessions", "session_id", "transactions", "session_id")es = es.add_relationship("customers", "customer_id", "sessions", "customer_id")# 查看实体集摘要es
输出会展示实体数量、每个实体的行数以及它们之间的关系。
2.2 运行深度特征合成
现在,我们以 customers 为目标实体,自动生成特征矩阵:
# 运行DFSfeature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name="customers", # 目标实体 max_depth=2, # 最大深度,控制特征复杂度 agg_primitives=["mean", "sum", "count", "std"], # 聚合基元 trans_primitives=["month", "hour", "weekday"], # 转换基元 verbose=True,)print(f"生成的样本数: {feature_matrix.shape[0]}, 特征数: {feature_matrix.shape[1]}")feature_matrix.head()
运行后,你会看到为每个客户生成了大量统计特征,例如:
COUNT(sessions):每个客户的会话数量
MEAN(transactions.amount):该客户所有交易的平均金额
MODE(sessions.device):最常使用的设备类型
2.3 理解“深度特征”
当 max_depth=2 时,DFS 会堆叠基元产生更复杂的特征。例如,MEAN(sessions.SUM(transactions.amount)) 的含义是:
先对每个会话内的交易金额求和(SUM)
再对同一客户的所有会话求平均值(MEAN)
这样就得到了“客户平均每个会话的消费总额”这一有意义的特征。
3. 进阶应用:自定义基元
当内置基元不够用时,Featuretools 允许你定义自己的转换或聚合函数。
3.1 自定义转换基元:统计字符串中指定单词出现次数
假设我们有一个文本字段,想统计某个单词出现的次数:
import pandas as pdimport featuretools as ftfrom featuretools.primitives import make_trans_primitivefrom woodwork.logical_types import NaturalLanguage, Double# 定义计算函数def string_count(column, string=None): """计算字符串中指定子串的出现次数""" assert string is not None, "需要指定要计数的字符串" return [text.lower().count(string) for text in column]# 自定义生成名称的函数,让特征名更有意义def string_count_generate_name(self, base_feature_names): return f'STRING_COUNT("{self.kwargs["string"]}")'# 创建自定义基元StringCount = make_trans_primitive( function=string_count, input_types=[NaturalLanguage], return_type=Double, cls_attributes={"generate_name": string_count_generate_name})
现在可以将这个基元用于 DFS:
feature_matrix, _ = ft.dfs( entityset=es, target_dataframe_name="customers", trans_primitives=[StringCount(string="the")], # 统计"the"的出现次数 max_depth=1,)
3.2 自定义转换基元:多输出特征
有些转换需要返回多个值,比如同时统计大小写字母个数。Featuretools 也支持这种多输出场景:
import reimport numpy as npfrom featuretools.primitives import make_trans_primitivedef case_count(array): """返回大写字母个数和小写字母个数""" upper = np.array([len(re.findall('[A-Z]', i)) for i in array]) lower = np.array([len(re.findall('[a-z]', i)) for i in array]) return [upper, lower] # 返回列表,每个元素是一个输出CaseCount = make_trans_primitive( function=case_count, input_types=[NaturalLanguage], return_type=Double, number_output_features=2 # 指定输出个数)
使用后,特征矩阵中会出现两列:CASE_COUNT(feature)[0] 和 CASE_COUNT(feature)[1]。
4. 单表场景的用法
即使只有一张表,Featuretools 也能发挥价值,尤其是当表内存在时间序列或多层级关系时。
import pandas as pdimport featuretools as ft# 假设有一张销售记录表sales_df = pd.DataFrame({ 'order_id': range(100), 'customer_id': [i % 10 for i in range(100)], 'amount': np.random.randn(100) * 50 + 100, 'order_date': pd.date_range('2023-01-01', periods=100, freq='D')})# 创建实体集并添加单个实体es = ft.EntitySet(id="single_table")es = es.add_dataframe( dataframe_name="orders", dataframe=sales_df, index="order_id", time_index="order_date")# 运行DFS,以订单表为目标feature_matrix, _ = ft.dfs( entityset=es, target_dataframe_name="orders", agg_primitives=["mean", "sum", "count"], trans_primitives=["month", "day", "weekday"], max_depth=1,)
此时 DFS 会:
对每个订单,从时间列提取月份、星期几等特征
如果通过 add_relationship 定义了父子关系(比如订单和商品明细),则可以生成聚合特征
5. 安装与性能优化
5.1 安装
# 基础安装pip install featuretools# 安装完整版(包含Dask支持、NLP基元等)pip install "featuretools[complete]"# 或通过 conda 安装conda install -c conda-forge featuretools
5.2 并行加速
DFS 支持多核并行计算,可通过 n_jobs 参数开启:
feature_matrix, _ = ft.dfs( entityset=es, target_dataframe_name="customers", n_jobs=-1, # 使用所有可用核心 max_depth=2,)
对于超大数据集,还可以结合 Dask 实现分布式计算。
6. 实际应用价值
Featuretools 生成的数百个特征可以直接输入到机器学习模型中。以 BigMart 销售预测为例,使用 Featuretools 自动生成特征后,模型在验证集上的 RMSE 从 1103 降低到 1092,并且公共排行榜分数也从 1183 提升到 1155。这种提升来自自动化特征工程挖掘出的深层统计信息,例如跨门店的商品聚合特征、时间趋势特征等。
总结
Featuretools 通过 EntitySet 组织关系数据,用深度特征合成自动生成复杂的统计特征,并支持自定义基元扩展功能。它的优势在于:
自动化:无需手动编写聚合代码
关系型数据友好:天然支持多表关联
可解释性:生成的特征名称清晰描述了构造过程
可扩展:支持自定义基元和并行计算
当你面对多表数据或需要大量统计特征时,Featuretools 是一个值得尝试的自动化工具。