【问题标题】:Pandas temporal cumulative sum by groupPandas 按组的时间累积总和
【发布时间】:2014-07-13 05:16:29
【问题描述】:

我有一个数据框,其中为每个 ID 记录了 1 个或多个事件。对于每个事件,都会记录 id、度量 x 和日期。像这样的:

import pandas as pd
import datetime as dt
import numpy as np
x = range(0, 6)
id = ['a', 'a', 'b', 'a', 'b', 'b']
dates = [dt.datetime(2012, 5, 2),dt.datetime(2012, 4, 2),dt.datetime(2012, 6, 2),
         dt.datetime(2012, 7, 30),dt.datetime(2012, 4, 1),dt.datetime(2012, 5, 9)]

df =pd.DataFrame(np.column_stack((id,x,dates)), columns = ['id', 'x', 'dates'])

我希望能够设置一个回溯期(即 70 天),并为数据集中的每一行计算该 id 和所需回溯范围内的任何先前事件的 x 的累积总和(不包括 x对于正在执行计算的行)。 最终应该看起来像:

  id  x                dates    want
0  a  0  2012-05-02 00:00:00    1
1  a  1  2012-04-02 00:00:00    0
2  b  2  2012-06-02 00:00:00    9
3  a  3  2012-07-30 00:00:00    0
4  b  4  2012-04-01 00:00:00    0
5  b  5  2012-05-09 00:00:00    4

【问题讨论】:

    标签: python pandas group-by time-series


    【解决方案1】:

    嗯,一种方法如下:(1) 使用“id”作为分组变量执行groupby/apply。 (2)在apply内,resample组到一个每日时间序列。 (3) 然后只需使用rolling_sum (并移位,因此您不包括当前行的“x”值)来计算 70 天回溯期的总和。 (4) 将组归约到只有原来的观察:

    In [12]: df = df.sort(['id','dates'])
    In [13]: df
    Out[13]: 
      id  x      dates
    1  a  1 2012-04-02
    0  a  0 2012-05-02
    3  a  3 2012-07-30
    4  b  4 2012-04-01
    5  b  5 2012-05-09
    2  b  2 2012-06-02
    

    您将需要按['id','dates'] 排序的数据。现在我们可以做groupby/apply

    In [15]: def past70(g):
                 g = g.set_index('dates').resample('D','last')
                 g['want'] = pd.rolling_sum(g['x'],70,0).shift(1)
                 return g[g.x.notnull()]            
    
    In [16]: df = df.groupby('id').apply(past70).drop('id',axis=1)
    In [17]: df
    Out[17]: 
                   x  want
    id dates              
    a  2012-04-02  1   NaN
       2012-05-02  0     1
       2012-07-30  3     0
    b  2012-04-01  4   NaN
       2012-05-09  5     4
       2012-06-02  2     9
    

    如果您不想要 NaN,那么就这样做:

    In [28]: df.fillna(0)
    Out[28]: 
                   x  want
    id dates              
    a  2012-04-02  1     0
       2012-05-02  0     1
       2012-07-30  3     0
    b  2012-04-01  4     0
       2012-05-09  5     4
       2012-06-02  2     9
    

    编辑:如果您想让回溯窗口成为参数,请执行以下操作:

    def past_window(g,win=70):
        g = g.set_index('dates').resample('D','last')
        g['want'] = pd.rolling_sum(g['x'],win,0).shift(1)
        return g[g.x.notnull()]            
    
    df = df.groupby('id').apply(past_window,win=10)
    print df.fillna(0)
    

    【讨论】:

    • 谢谢,这似乎做到了!如果我希望 70 作为过去函数的参数(即 def past(g,lookback)),那么我该如何将该参数传递给 .apply(past)?
    • 它只是成为apply 中的下一个参数。详情见我编​​辑。
    【解决方案2】:

    我需要执行类似的操作,所以我查看了一下并在 pandas 的食谱中找到了(我强烈推荐给任何愿意了解这个包的所有巨大可能性的人)这个页面:Pandas: rolling mean by time interval。使用最新版本的 pandas,您可以将用于计算窗口的附加参数传递给基于 date_time 列的 rolling() 函数。所以这个例子变得更简单了:

    # First, convert the dates to date time to make sure it's compatible
    df['dates'] = pd.to_datetime(df['dates'])
    
    # Then, sort the time series so that it is monotonic
    df.sort_values(['id', 'dates'], inplace=True)
    
    # '70d' corresponds to the the time window we are considering
    # The 'closed' parameter indicates whether to include the interval bounds
    # 'yearfirst' indicates to pandas the format of your time series
    df['want'] = df.groupby('id').rolling('70d', on='dates', closed='neither'
        )['x'].sum().to_numpy()
    
    df['want'] = np.where(df['want'].isnull(), 0, df['want']).astype(int)
    df.sort_index() # to dispay it in the same order as the example provided
      id  x      dates  want
    0  a  0 2012-05-02     1
    1  a  1 2012-04-02     0
    2  b  2 2012-06-02     9
    3  a  3 2012-07-30     0
    4  b  4 2012-04-01     0
    5  b  5 2012-05-09     4
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-15
      • 1970-01-01
      • 1970-01-01
      • 2015-09-11
      • 2018-05-30
      • 2020-03-04
      • 2021-11-17
      • 2021-06-18
      相关资源
      最近更新 更多