使用 pandas 的滚动窗口计算一天中每个时间的平均值

Posted

技术标签:

【中文标题】使用 pandas 的滚动窗口计算一天中每个时间的平均值【英文标题】:Calculating the mean for each time of day with a rolling window with pandas 【发布时间】:2021-07-24 14:49:22 【问题描述】:

我有一个 pandas 数据框,它有一个日期时间索引和四列 Phase 1Phase 2Phase 3Sum。数据经过预处理,每 15 分钟有一行,持续数月。数据非常循环,几乎每天都在重复,但随着时间的推移变化缓慢。目标是在过去一周(或其他时间范围)的某个时间为所有天生成值的平均值。 (用于机器学习任务)

我已经设法使用以下代码计算了一天中每个时间的平均值:(这会产生一个 1 天长的数据帧)

df.groupby(df.index.hour * 60 + df.index.minute).mean()
        Phase 1    Phase 2    Phase 3        Sum
Time                                            
0     10.105782  10.235237   9.990037  30.331055
15    10.106374  10.116440   9.991424  30.214238
30    10.106517  10.086310  10.003420  30.196246
45    10.128441  10.249100  10.032895  30.410436
...
1410  10.112582  10.643766   9.971592  30.727939
1425  10.102739  10.372299   9.969986  30.445025

虽然数据是逐渐变化的,但这个总天数的平均值并不是很好。如果我可以计算这种类型的平均值会更好,但只包括上周每天的数据。

到目前为止我尝试过的是:

df
  .groupby(df.index.hour * 60 + df.index.minute)
  .rolling("7D", closed="left")
  .mean()

它生成正确的数据,但缺少日期信息(需要保留以供将来计算)并且行的顺序错误。

        Phase 1    Phase 2    Phase 3        Sum
Time                                            
0           NaN        NaN        NaN        NaN
0     10.064458  10.051470  10.177814  30.293742
0     10.043804   9.983143  10.062019  30.088965
0     10.020861   9.917236  10.000181  29.938278
...
0     10.224965  10.507418  10.030670  30.763053
0     10.155706  10.396408   9.919538  30.471651
0     10.149112  10.352153   9.894257  30.395522
0     10.144540  10.349998   9.902504  30.397042
15          NaN        NaN        NaN        NaN
15    10.061673   9.967295  10.143008  30.171976
15    10.059581  10.158814  10.051835  30.270230
15     9.995112  10.024808   9.999054  30.018974
...

还有NaNs 在第一天没有完全呈现时出现的问题。是否需要先删除不完整的天数,还是可以将它们合并到平均值中?

我也试过这个:

df
  .groupby([
    pd.Grouper(freq="1D"),
    df.index.hour * 60 + df.index.minute
  ])
  .rolling("7D", closed="left")
  .mean()

但它会生成一个仅包含 NaNs 的数据框,所以一定是出了点问题。

结果应该是这样的:

                       Phase 1    Phase 2    Phase 3        Sum
Time                                                           
2021-02-13 00:00:00  11.882597  12.779326  12.458625  37.120549
2021-02-13 00:15:00  11.866148  12.871785  12.509614  37.247547
2021-02-13 00:30:00  11.713676  12.730861  12.525868  36.970405
2021-02-13 00:45:00  11.742079  12.697406  12.592411  37.031897
2021-02-13 01:00:00  11.765234  12.848741  12.622687  37.236662
...
2021-05-01 10:30:00  11.842673  12.190760  12.572203  36.605636
2021-05-01 10:45:00  11.837964  12.118095  12.611271  36.567331
2021-05-01 11:00:00  11.827275  12.220564  12.588131  36.635970

在本例中,第二行包含2021-02-13 00:15:002021-02-12 00:15:00、...、2021-02-07 00:15:00 的平均值。我对编程并不陌生,但对 python 和 pandas 相对较新,因此非常感谢任何帮助和提示。

【问题讨论】:

【参考方案1】:

您可以对数据集进行预过滤,使其仅包含 dt 日期之前的 13 天,然后是 groupby 时间,rollingmin_periods=7 需要 7 天,使用 meandropna 删除日期前几天的累计值少于 7 天:

# generate sample dataset
ix = pd.date_range('2021-01-01', '2021-05-01', freq='15min')
df = pd.DataFrame(
        'Phase1': np.random.uniform(0, 1, len(ix)),
        'Phase2': np.random.uniform(0, 1, len(ix)),
        'Phase3': np.random.uniform(0, 1, len(ix)),
    , index=ix)
df['Sum'] = df.sum(1)

# set max date
dt = pd.to_datetime('2021-02-14')

# filter out values in [dt - 13 days, dt)
z = df.loc[(df.index >= dt - pd.DateOffset(days=13)) & (df.index < dt)]

# calculate 7-day rolling average for the same time of the day
# for 7 days preceding `dt`
(z
     .groupby(z.index.time)
     .rolling('7d', min_periods=7)
     .mean()
     .dropna()
     .droplevel(0)
     .sort_index())

输出:

                       Phase1    Phase2    Phase3       Sum
2021-02-07 00:00:00  0.479466  0.731746  0.503017  1.714229
2021-02-07 00:15:00  0.443550  0.423135  0.543204  1.409889
2021-02-07 00:30:00  0.465272  0.626117  0.454462  1.545851
2021-02-07 00:45:00  0.528733  0.433475  0.386822  1.349029
2021-02-07 01:00:00  0.425309  0.360065  0.488509  1.273884
...                       ...       ...       ...       ...
2021-02-13 22:45:00  0.519717  0.490549  0.524330  1.534596
2021-02-13 23:00:00  0.367935  0.460093  0.373338  1.201366
2021-02-13 23:15:00  0.597424  0.438130  0.478259  1.513813
2021-02-13 23:30:00  0.675142  0.443580  0.330791  1.449514
2021-02-13 23:45:00  0.474604  0.355723  0.596467  1.426794

【讨论】:

那么我怎样才能用它来计算每一天呢?也许我的描述不清楚,但应该计算所有天的上周平均值,而不仅仅是最后一天。 @douira 是的,对不起,那我误解了你的问题,让我更新... 是我的错,介绍写得有点奇怪,我想现在更清楚了。如果仍然令人困惑,请告诉我。 @douira 好的,请查看最近 7 天一天中同一时间的 7 天滚动平均值的更新版本 效果很好!我只是使用你代码的最后一部分,从groupby 开始,因为我想整天处理。感谢您的帮助。

以上是关于使用 pandas 的滚动窗口计算一天中每个时间的平均值的主要内容,如果未能解决你的问题,请参考以下文章

python pandas 按一天中的小时求和

Pandas groupby 一天中的时间到字典

在 pandas 中以更快的方式分组一天中的时间

Pandas - Python 2.7:如何将时间序列索引转换为一天中的秒数?

pandas groupby 一天中的时间,带 15 分钟的垃圾箱

查找一天中事件的开始时间和结束时间 - Pandas 时间序列 - 这样结束时间不会落入第二天