【问题标题】:Pandas - Rolling window - uneven interval熊猫 - 滚动窗口 - 不均匀间隔
【发布时间】:2016-09-10 01:06:48
【问题描述】:

我在计算适用于此数据集中每个买家的交易的滚动计数时遇到问题,其结构如下:

userID  itemID      transaction_ts
3229    4493320     2016-01-02 14:55:00
3229    4492492     2016-01-02 14:57:02
3229    4496756     2016-01-04 09:01:18
3229    4493673     2016-01-04 09:11:10
3229    4497531     2016-01-04 11:05:25
3229    4495006     2016-01-05 07:25:11
4330    4500695     2016-01-02 09:17:21
4330    4500656     2016-01-03 09:19:28
4330    4503087     2016-01-04 07:42:15
4330    4501846     2016-01-04 08:55:24
4330    4504105     2016-01-04 09:59:35

理想情况下,滚动事务计数窗口如下所示,例如24 小时:

userID  itemID      transaction_ts        rolling_count
3229    4493320     2016-01-02 14:55:00         1
3229    4492492     2016-01-02 14:57:02         2
3229    4496756     2016-01-04 09:01:18         1
3229    4493673     2016-01-04 09:11:10         2
3229    4497531     2016-01-04 11:05:25         3
3229    4495006     2016-01-05 07:25:11         4
4330    4500695     2016-01-02 09:17:21         1
4330    4500656     2016-01-03 09:19:28         1
4330    4503087     2016-01-04 07:42:15         2 
4330    4501846     2016-01-04 08:55:24         3
4330    4504105     2016-01-04 09:59:35         3

这里有一个类似问题的绝佳答案:pandas rolling sum of last five minutes

但是,此答案仅取决于时间戳字段,与上述不同,滚动计数必须在遇到来自不同用户的事务时重置为 1 到上面的行。可以通过切片找到解决方案,但考虑到此数据集的大小(可能超过 1m 行),这是不可行的。

至关重要的是,窗口应该反映相应行的 transactional_ts 之前的 24 小时,因此为什么我认为自定义 df.apply 或 rolling_window 方法是合适的,我只是不知道如何根据用户 ID。

【问题讨论】:

  • 您可以使用df.groupby('userID'),然后应用(例如使用.transform())您的自定义滚动功能。对于大型数据框,聚合/转换仍然需要一段时间。顺便说一句,你打算以 1 分钟的频率重新采样你的 df 吗?
  • 你说得对,我认为答案必须涉及链接解决方案中的 df.groupby('userID') 和一些自定义函数。我认为重新采样到 1 分钟只会使数据框变得庞大,可能更好地重新采样到 1 天,看看我能做什么。我有点希望它能够处理每个单独的事务 ts,而不是为计算简洁而选择的任意时间间隔。谢谢!
  • 我只是不知道如何在不扭曲数据的情况下进行上采样。但是请看我的答案以了解不同的方法。

标签: python pandas time-series


【解决方案1】:

解决方案的一部分(滚动 cumsum)可能已经是 here。 (我只是改变了滞后的类型):

from datetime import timedelta

def msum(s, lag):
    lag = s.index - timedelta(days=lag)
    inds = np.searchsorted(s.index.astype(np.int64), lag.astype(np.int64))
    cs = s.cumsum()
    return pd.Series(cs.values - cs[inds].values + s[inds].values, index=s.index)

该函数要求索引为日期时间类型。此外,每个 userID 组中的索引应该已经排序(例如在您的示例中)。

df = df.set_index('transaction_ts')
df['rolling_count'] = 1
df['rolling_count'] = df.groupby('userID', sort=False)['rolling_count'].transform(lambda x : msum(x,1))

groupby 选项sort=False 可能会加快速度。 (它负责对组键进行排序。)

【讨论】:

  • 您将如何调整此方法以获取窗口内唯一值的数量而不是累积总和?
  • 这可能会回答我的问题:df.groupby('UserID', sort=False)['column_name'].transform(lambda x : x.rolling('3D').apply(lambda x : len(np.unique(x))))
【解决方案2】:

我设法得到了一个至少在测试集上有效的解决方案。 ptjr 最先到达那里!这个问题的第一个解决方案Pandas Rolling Computations on Sliding Windows (Unevenly spaced) 帮了大忙。

正如 ptrj 之前指出的 - 使用 df.groupby('userID') 是关键。

df = pd.read_excel('velocity.xlsx') # reading dataframe in
df = df.sort_values(['userID','transaction_ts'])
df = df.reset_index(drop=True) # ensure index is sorted according to userID|transaction_ts
df['ones'] = 1

def add_rolling_count(x,number_of_hours):
    x['lag'] = x['transaction_ts'] - timedelta(hours=number_of_hours)
    inds = np.searchsorted(np.array(x['transaction_ts'].astype(np.int64)),   np.array(x['lag'].astype(np.int64)))
    cs = x['ones'].reset_index(drop=True).cumsum()
    x['count'] = cs.values - cs[inds].values + 1
    return x`

df = df.groupby('user_id').apply(lambda x: add_rolling_count(x, 24))

【讨论】:

    猜你喜欢
    • 2013-01-15
    • 2013-03-24
    • 2018-01-14
    • 2017-03-30
    • 2019-10-21
    • 2020-04-21
    • 2021-03-30
    相关资源
    最近更新 更多