Pandas DataFrame多索引groupby滚动操作中遇到缺失日期

5
我有一个数据框,其中具有MultiIndex,其中索引的最后一列是日期。我试图对具有特定频率的列进行滚动操作。据我所知,如果我有一个TimeIndex,通常的pandas方法是使用频率字符串(例如'2D'表示窗口为两天)调用rolling函数。另一种建议是对TimeIndex进行重新采样,然后使用整数2应用rolling函数。本质上,我想要做到的是除了最后一列之外,按所有列进行分组,然后告诉rolling列使用最后一列进行timedelta-specific rolling。以下是一个示例以说明这一点:
from datetime import datetime
import pandas as pd
multi_index = pd.MultiIndex.from_tuples([
    ("A", datetime(2017, 1, 1)), 
    ("A", datetime(2017, 1, 2)), 
    ("A", datetime(2017, 1, 3)), 
    ("A", datetime(2017, 1, 4)),
    ("B", datetime(2017, 1, 1)),
    ("B", datetime(2017, 1, 3)),
    ("B", datetime(2017, 1, 4))])
df = pd.DataFrame(index=multi_index, data={"colA": [1, 1, 1, 1, 1, 1, 1]})
display(df)
df.groupby([df.index.get_level_values(0), pd.Grouper(freq="1D", level=-1)]).sum().rolling(2).sum

上述代码并未创建一个行(B, datetime(2017, 1, 2)),因此滚动求和值将全部为两个。

一种不太优雅的解决方法是在滚动之前对数据进行unstack、fillna和stack操作,这仅适用于存在拥有所有日期的分组情况。

df.groupby([df.index.get_level_values(0), pd.Grouper(freq="1D", level=-1)]
).sum().unstack().fillna(0).stack().rolling(2).sum()

毫无疑问,这是一种丑陋的hack方法,速度缓慢且容易出错。是否有更好的方法来实现我在此处所需的功能,而不需要进行广泛的操作呢?理想情况下,是否有某种方式可以告诉分组器使用时间戳列或自行填充缺失值呢?
1个回答

6

您可以使用 groupby + resample + fillna - 需要版本 pandas 0.19.0

multi_index = pd.MultiIndex.from_tuples([
    ("A", datetime(2017, 1, 1)), 
    ("A", datetime(2017, 1, 2)), 
    ("A", datetime(2017, 1, 3)), 
    ("A", datetime(2017, 1, 4)),
    ("B", datetime(2017, 1, 1)),
    ("B", datetime(2017, 1, 3)),
    ("B", datetime(2017, 1, 4))])
df = pd.DataFrame(index=multi_index, data={"colA": [1, 2, 3, 4, 1, 2, 3]})
print (df)
              colA
A 2017-01-01     1
  2017-01-02     2
  2017-01-03     3
  2017-01-04     4
B 2017-01-01     1
  2017-01-03     2
  2017-01-04     3

b = df.groupby(level=0).resample('1D', level=1).sum().fillna(0).rolling(2).sum()
print (b)
              colA
A 2017-01-01   NaN
  2017-01-02   3.0
  2017-01-03   5.0
  2017-01-04   7.0
B 2017-01-01   5.0
  2017-01-02   1.0
  2017-01-03   2.0
  2017-01-04   5.0

非常棒的答案,但是我想把第一个B改成Nan(因为它是一个新组)...使用您的代码,我能够做到这一点:df.groupby(level=0).resample('1D', level=1).sum().fillna(0).groupby(level=0).apply(lambda x: x.rolling(2).sum()) - Sam Cohan
但是如果使用 df.groupby([df.index.get_level_values(0), pd.Grouper(freq="1D", level=-1)]).sum().unstack().fillna(0).stack().rolling(2).sum(),则可以得到相同的输出。 - jezrael
你说得对。你的回答完美地做到了我所询问的。显然我给了一个赞,因为这非常有帮助(我在这里是个菜鸟,所以我的赞好像并不算数!)非常感谢你的帮助! - Sam Cohan
我有一个类似的问题,只是稍作修改:如何使滚动总和“重置”,即在'B'(在框架b中)中的colA的第一个值应为NaN而不是5?基本上,对于索引级别0中的每个项目,分别计算日期间的滚动总和,而不重叠。 - SanMu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接