在 Python 特征工具中从特征工程中排除当前行

Excluding the current row from feature engineering in Python featuretools

我正在使用 featuretools 为当前行生成历史特征。例如,会话期间最后一小时的交易数量。

featuretools 包含参数 cutoff_time 以及时排除 cutoff_time 之后的所有行。

我将 cutoff_time 设置为 time_index value - 1 second,因此我希望特征基于历史数据减去当前行。这允许包括来自历史行的响应变量。

问题是,当这个参数不等于 time_index 变量时,我在原始 生成的特征中得到一堆 NaNs .

示例:

#!/usr/bin/env python3

import featuretools as ft
import pandas as pd
from featuretools import primitives, variable_types

data = ft.demo.load_mock_customer()

transactions_df = data['transactions']
transactions_df['cutoff_time'] = transactions_df['transaction_time'] - pd.Timedelta(seconds=1)

es = ft.EntitySet('transactions_set')
es.entity_from_dataframe(
    entity_id='transactions',
    dataframe=transactions_df,
    variable_types={
        'transaction_id': variable_types.Index,
        'session_id': variable_types.Id,
        'transaction_time': variable_types.DatetimeTimeIndex,
        'product_id': variable_types.Id,
        'amount': variable_types.Numeric,
        'cutoff_time': variable_types.Datetime
    },
    index='transaction_id',
    time_index='transaction_time'
)
es.normalize_entity(
    base_entity_id='transactions',
    new_entity_id='sessions',
    index='session_id'
)
es.add_last_time_indexes()

fm, features = ft.dfs(
    entityset=es,
    target_entity='transactions',
    agg_primitives=[primitives.Sum, primitives.Count],
    trans_primitives=[primitives.Day],
    cutoff_time=transactions_df[['transaction_id', 'cutoff_time']].
        rename(index=str, columns={'transaction_id': 'transaction_id', 'cutoff_time': 'time'}),
    training_window='1 hours',
    verbose=True
)

print(fm)

输出(摘录):

                DAY(cutoff_time)  sessions.SUM(transactions.amount)  \
transaction_id                                                        
352                          NaN                                NaN   
186                          NaN                                NaN   
319                          NaN                                NaN   
256                          NaN                                NaN   
449                          NaN                                NaN   
40                           NaN                                NaN   
13                           NaN                                NaN   
127                          NaN                                NaN   
21                           NaN                                NaN   
309                          NaN                                NaN   

sessions.SUM(transactions.amount) 应该 >= 0。原始特征 session_id product_id amount 也都是 NaN

如果 transactions_df['cutoff_time'] = transactions_df['transaction_time'](无时间增量),此代码有效但包含当前行。

计算将从计算中排除当前行的聚合和转换的正确方法是什么?

您看到的是截止时间和 time_index 的预期行为。实体的 time_index 表示第一次可以知道每个实例的任何信息。当您向 Featuretools 提供截止时间时,它会通过删除时间索引在截止时间之后的行来模拟数据集处的数据集状态。

在这种情况下,交易的 transaction_idsession_id 在交易时间之前是未知的,这是有道理的,因为交易还没有发生。这就是为什么当您要求 Featuretools 在交易时间前一秒计算特征时,它 returns NaN 用于所有特征。

处理这个问题的方法是分配一个secondary_time_index to variables like amount in transactions. This is described in the advanced solution of this Stack Overflow answer。这允许您告诉 Featuretools 特定变量在 transaction_time 时无效,只能在您的辅助时间索引列中使用。本质上,您将阻止某些行值在事务处理时被使用,同时允许其他值。您可以为该实体中的任意数量的变量分配辅助时间索引。

根据 Max Kanter 的回答:

#!/usr/bin/env python3

import featuretools as ft
import pandas as pd
from featuretools import primitives, variable_types

data = ft.demo.load_mock_customer()

transactions_df = data['transactions']
transactions_df['response_time'] = transactions_df['transaction_time'] + pd.Timedelta(seconds=1)

es = ft.EntitySet('transactions_set')
es.entity_from_dataframe(
    entity_id='transactions',
    dataframe=transactions_df,
    variable_types={
        'transaction_id': variable_types.Index,
        'session_id': variable_types.Id,
        'transaction_time': variable_types.DatetimeTimeIndex,
        'product_id': variable_types.Id,
        'amount': variable_types.Numeric,
        'response_time': variable_types.Datetime
    },
    index='transaction_id',
    time_index='transaction_time',
    secondary_time_index={'response_time': ['amount', 'transaction_id']}
)
es.normalize_entity(
    base_entity_id='transactions',
    new_entity_id='sessions',
    index='session_id'
)
es.add_last_time_indexes()

fm, features = ft.dfs(
    entityset=es,
    target_entity='transactions',
    agg_primitives=[primitives.Sum, primitives.Count],
    trans_primitives=[primitives.Day],
    cutoff_time=transactions_df[['transaction_id', 'transaction_time']],
    cutoff_time_in_index=True,
    training_window='5 minutes',
    verbose=True
)

print(fm)

此代码生成的特征 sessions.SUM(transactions.amount)sessions.COUNT(transactions) 排除了当前行,并包含所有之前不到 5 分钟的行。