【问题标题】:Time-based .rolling() fails with group by基于时间的 .rolling() 因分组失败而失败
【发布时间】:2017-04-10 20:30:30
【问题描述】:

这是来自Pandas Issue #13966的代码sn-p

dates = pd.date_range(start='2016-01-01 09:30:00', periods=20, freq='s')
df = pd.DataFrame({'A': [1] * 20 + [2] * 12 + [3] * 8,
                   'B': np.concatenate((dates, dates)),
                   'C': np.arange(40)})

失败:

df.groupby('A').rolling('4s', on='B').C.mean()
ValueError: B must be monotonic

根据上面链接的问题,这似乎是一个错误。有没有人有好的解决方法?

【问题讨论】:

    标签: python pandas group-by


    【解决方案1】:

    先设置B为索引,以便在其上使用Groupby.resample方法。

    df.set_index('B', inplace=True)
    

    Groupby A 并根据秒频率重新采样。由于 resample 不能直接用于滚动,请使用 ffill(forward fillna with NaN limit 为 0)。 现在使用rolling 函数,将窗口大小指定为4(因为freq=4s)间隔,并沿C 列取平均值,如图所示:

    for _, grp in df.groupby('A'):
        print (grp.resample('s').ffill(limit=0).rolling(4)['C'].mean().head(10)) #Remove head() 
    

    得到的结果输出:

    B
    2016-01-01 09:30:00    NaN
    2016-01-01 09:30:01    NaN
    2016-01-01 09:30:02    NaN
    2016-01-01 09:30:03    1.5
    2016-01-01 09:30:04    2.5
    2016-01-01 09:30:05    3.5
    2016-01-01 09:30:06    4.5
    2016-01-01 09:30:07    5.5
    2016-01-01 09:30:08    6.5
    2016-01-01 09:30:09    7.5
    Freq: S, Name: C, dtype: float64
    B
    2016-01-01 09:30:00     NaN
    2016-01-01 09:30:01     NaN
    2016-01-01 09:30:02     NaN
    2016-01-01 09:30:03    21.5
    2016-01-01 09:30:04    22.5
    2016-01-01 09:30:05    23.5
    2016-01-01 09:30:06    24.5
    2016-01-01 09:30:07    25.5
    2016-01-01 09:30:08    26.5
    2016-01-01 09:30:09    27.5
    Freq: S, Name: C, dtype: float64
    B
    2016-01-01 09:30:12     NaN
    2016-01-01 09:30:13     NaN
    2016-01-01 09:30:14     NaN
    2016-01-01 09:30:15    33.5
    2016-01-01 09:30:16    34.5
    2016-01-01 09:30:17    35.5
    2016-01-01 09:30:18    36.5
    2016-01-01 09:30:19    37.5
    Freq: S, Name: C, dtype: float64
    

    TL;DR

    在适当设置索引后使用groupby.apply 作为解决方法:

    # tested in version - 0.19.1
    df.groupby('A').apply(lambda grp: grp.resample('s').ffill(limit=0).rolling(4)['C'].mean())
    

    (或)

    # Tested in OP's version - 0.19.0
    df.groupby('A').apply(lambda grp: grp.resample('s').ffill().rolling(4)['C'].mean())
    

    两者都有效。

    【讨论】:

    • 为什么在 ffill 中设置 limit=0?例如,如果我在创建原始数据框时更改了 freq='2s' ,那么您的示例就会中断。但是,如果我将 ffill 更改为没有参数,这似乎可行。
    • 更具体地说,我认为正确的代码是:df.groupby('A').apply(lambda grp: grp.resample('s').ffill().rolling(4)['C'].mean())
    • 不,它不会对我造成破坏。提供limit=0 可确保在前向填充操作期间不会填充NaN 元素。也许它可以在没有分配int 值的情况下工作,默认为limit=None。但为了更安全,只需使用 arg。是的,最后提到的是您要查找的语法。
    • 当我说中断时,我的意思是我在输出中只得到 NaN。需要明确的是,这是我在执行 limit=0 时得到的输出:B 2016-01-01 09:30:00 NaN 2016-01-01 09:30:01 NaN 2016-01-01 09:30:02 NaN 2016-01-01 09:30:03 NaN 2016-01-01 09:30:04 NaN 2016-01-01 09:30:05 NaN 2016-01-01 09:30:06 NaN 2016-01-01 09:30:07 NaN 2016-01-01 09:30:08 NaN 2016-01-01 09:30:09 NaN
    • 所有Nans!你是哪个版本的pandas?要检查,请执行以下操作:print (pd.__version__)
    【解决方案2】:
    >>> df.sort_values('B').set_index('B').groupby('A').rolling('4s').C.mean()
    

    【讨论】:

      猜你喜欢
      • 2018-02-16
      • 2010-12-28
      • 1970-01-01
      • 1970-01-01
      • 2012-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多