【问题标题】:Python - rolling functions for GroupBy objectPython - GroupBy 对象的滚动函数
【发布时间】:2012-12-09 09:32:28
【问题描述】:

我有一个<pandas.core.groupby.SeriesGroupBy object at 0x03F1A9F0> 类型的时间序列对象groupedgrouped.sum() 给出了预期的结果,但我无法让 rolling_sum 与 groupby 对象一起使用。有没有办法将滚动函数应用于groupby 对象?例如:

x = range(0, 6)
id = ['a', 'a', 'a', 'b', 'b', 'b']
df = DataFrame(zip(id, x), columns = ['id', 'x'])
df.groupby('id').sum()
id    x
a    3
b   12

但是,我想要类似的东西:

  id  x
0  a  0
1  a  1
2  a  3
3  b  3
4  b  7
5  b  12

【问题讨论】:

  • 您希望滚动函数如何在分组对象上工作(我的意思是用符号写出您想要做的数学运算)?
  • 对不起,我应该更清楚。
  • 所以你想在每个组上做一个cumsum,然后把整个东西缝合到一个数据框中?
  • 是的,理想情况下是 cumsum 和任何滚动函数(均值、总和、标准差)。

标签: python pandas pandas-groupby rolling-computation rolling-sum


【解决方案1】:

累计金额

为了直接回答这个问题,cumsum 方法将产生所需的系列:

In [17]: df
Out[17]:
  id  x
0  a  0
1  a  1
2  a  2
3  b  3
4  b  4
5  b  5

In [18]: df.groupby('id').x.cumsum()
Out[18]:
0     0
1     1
2     3
3     3
4     7
5    12
Name: x, dtype: int64

pandas 每组滚动函数

更一般地,任何滚动功能都可以按如下方式应用于每个组(使用@kekert 评论的新 .rolling 方法)。请注意,返回类型是多索引系列,这与之前(已弃用)的 pd.rolling_* 方法不同。

In [10]: df.groupby('id')['x'].rolling(2, min_periods=1).sum()
Out[10]:
id
a   0   0.00
    1   1.00
    2   3.00
b   3   3.00
    4   7.00
    5   9.00
Name: x, dtype: float64

要应用每组滚动功能并以原始数据帧顺序接收结果,应使用转换:

In [16]: df.groupby('id')['x'].transform(lambda s: s.rolling(2, min_periods=1).sum())
Out[16]:
0    0
1    1
2    3
3    3
4    7
5    9
Name: x, dtype: int64

不推荐使用的方法

作为参考,以下是现已弃用的 pandas.rolling_mean 的行为方式:

In [16]: df.groupby('id')['x'].apply(pd.rolling_mean, 2, min_periods=1)
Out[16]: 
0    0.0
1    0.5
2    1.5
3    3.0
4    3.5
5    4.5

【讨论】:

  • pd.rolling_mean 现在不推荐用于系列并将被删除,请改用df.groupby('id')['x'].rolling(2).mean()
  • 这个答案拯救了我的一天!
【解决方案2】:

如果您需要将分组滚动函数重新分配回原始数据框,同时保持顺序和分组,您可以使用transform 函数。

df.sort_values(by='date', inplace=True)
grpd = df.groupby('group_key')
#using center=false to assign values on window's last row
df['val_rolling_7_mean'] = grpd['val'].transform(lambda x: x.rolling(7, center=False).mean())

【讨论】:

    【解决方案3】:

    这是另一种很好地概括并使用pandas的@​​987654321@方法的方法。

    它非常高效,也非常适用于具有固定窗口的rolling window calculations,例如时间序列。

    # Import pandas library
    import pandas as pd
    
    # Prepare columns
    x = range(0, 6)
    id = ['a', 'a', 'a', 'b', 'b', 'b']
    
    # Create dataframe from columns above
    df = pd.DataFrame({'id':id, 'x':x})
    
    # Calculate rolling sum with infinite window size (i.e. all rows in group) using "expanding"
    df['rolling_sum'] = df.groupby('id')['x'].transform(lambda x: x.expanding().sum())
    
    # Output as desired by original poster
    print(df)
      id  x  rolling_sum
    0  a  0            0
    1  a  1            1
    2  a  2            3
    3  b  3            3
    4  b  4            7
    5  b  5           12
    

    【讨论】:

    • 你有什么可以证明这是“非常有效”的吗?通常对于 pandas,进行任何类型的迭代(例如,“transform”或“apply”)都会对性能造成重大影响,与使用矢量运算(“.sum”、“.滚动”等都将是)。我知道 Pandas 确实对迭代循环进行了一些预检查,看看它是否可以为您优化它,但如果性能是一个问题,通常应该避免迭代。
    • 很抱歉,我只能给你一个赞成票,我正在考虑创建新帐户以给予这个答案更多的信任。这是唯一一个对我有用的多列分组,谢谢!
    • 酷。这可以应用指数移动平均线。 q['exponential_ave'] = q.groupby('id')['x'].transform(lambda x: x.ewm(com=0.2).mean())
    • 使用expanding 与使用rolling 有什么区别?
    • @liang this article 比我能解释得更好。在滚动函数中,窗口大小保持不变,而在扩展函数中,它会发生变化。参见this answer
    【解决方案4】:

    对于遇到这个老问题的 Google 员工:

    关于@kekert 对@Garrett 使用新的回答的评论

    df.groupby('id')['x'].rolling(2).mean()
    

    而不是现在已弃用的

    df.groupby('id')['x'].apply(pd.rolling_mean, 2, min_periods=1)
    

    奇怪的是,新的 .rolling().mean() 方法似乎返回了一个多索引系列,首先由 group_by 列索引,然后是索引。然而,旧方法只会返回一个由原始 df 索引单独索引的系列,这可能不太有意义,但可以非常方便地将该系列作为新列添加到原始数据帧中。

    所以我想我已经找到了一个使用新 rolling() 方法并且仍然有效的解决方案:

    df.groupby('id')['x'].rolling(2).mean().reset_index(0,drop=True)
    

    应该给你系列

    0    0.0
    1    0.5
    2    1.5
    3    3.0
    4    3.5
    5    4.5
    

    您可以将其添加为列:

    df['x'] = df.groupby('id')['x'].rolling(2).mean().reset_index(0,drop=True)
    

    【讨论】:

    • 我认为你可以使用.transform 而不是reset_index?
    • 如果您按多列分组,这实际上会失败。删除第一个参数(级别)解决了这个问题,因为它默认删除了所有级别。所以这条线变成了df['x'] = df.groupby('id')['x'].rolling(2).mean().reset_index(drop=True)
    • 作为另一个令人抓狂的细微差别,如果您的组变量尚未排序,请使用groupby(..., sort=False)。将此滚动平均值添加为新列时,我得到了非常奇怪的结果,因为顺序与原始 df 不匹配。
    • 非常有用的信息。 a) 他们应该把这个添加到他们的pandas Cookbook b) 你能提出一些关于功能变化的 pandas 错误吗?他们应该在弃用之前更好地考虑后果。
    • 您能否详细说明为什么我们应该把.rolling(2),即为什么window=2 放在这里?是因为有 2 个组 'a' 和 'b'?
    【解决方案5】:

    我不确定机制,但这是可行的。注意,返回的值只是一个 ndarray。我认为您可以以这种方式应用任何累积或“滚动”函数,它应该具有相同的结果。

    我已经用cumprodcummaxcummin 对其进行了测试,它们都返回了一个ndarray。我认为 pandas 足够聪明,知道这些函数返回一个系列,因此该函数被用作转换而不是聚合。

    In [35]: df.groupby('id')['x'].cumsum()
    Out[35]:
    0     0
    1     1
    2     3
    3     3
    4     7
    5    12
    

    编辑:我很奇怪这种语法确实返回了一个系列:

    In [54]: df.groupby('id')['x'].transform('cumsum')
    Out[54]:
    0     0
    1     1
    2     3
    3     3
    4     7
    5    12
    Name: x
    

    【讨论】:

      猜你喜欢
      • 2015-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多