【问题标题】:Pandas rolling average window for dates excluding row不包括行的日期的 Pandas 滚动平均窗口
【发布时间】:2022-12-18 17:14:03
【问题描述】:
df = pd.DataFrame(
    {"date": [pd.Timestamp("2022-01-01"), pd.Timestamp("2022-01-01"), pd.Timestamp("2022-01-01"), pd.Timestamp("2022-01-03"), pd.Timestamp("2022-01-05")],
    "numbers": [1,2,3,4,5]
    }
)

如果我有以下 df 并且我想获得每行 date 列之前的 numbers 值的滚动平均值,我该怎么做?

我知道我能做到

df["av"] = df.shift(1).rolling(window=3).mean()

但这不会动态变化,所以它包括今天。

对于样本 df 的 3 天窗口,我对新的 av 列的预期输出是

    date    numbers av
0   2022-01-01  1   NaN
1   2022-01-01  2   NaN
2   2022-01-01  3   NaN
3   2022-01-03  4   2.0
4   2022-01-03  7   2.0
5   2022-01-05  5   5.5

【问题讨论】:

  • 5.5 怎么算?
  • 最近三天的平均值。在该行中,它是 1 月 3 日的两个数值。 (7 +4)/2 @jezrael

标签: pandas dataframe rolling-computation


【解决方案1】:

我认为您需要每个唯一的滚动方式 dates 添加排除日期移动 1 天。

根据定义,这里使用替代解决方案 - sum / count

df1 = (df.groupby('date')['numbers']
         .agg(['sum','size'])
         .asfreq('d', fill_value=0)
         .rolling(window=3, min_periods=1)
         .sum())

df['av'] = df['date'].map(df1['sum'].div(df1['size']).shift())
print (df)
        date  numbers   av
0 2022-01-01        1  NaN
1 2022-01-01        2  NaN
2 2022-01-01        3  NaN
3 2022-01-03        4  2.0
4 2022-01-03        7  2.0
5 2022-01-05        5  5.5

解释:

首先是聚合 sumsize 用于计数:

print (df.groupby('date')['numbers'].agg(['sum','size']))
            sum  size
date                 
2022-01-01    6     3
2022-01-03   11     2
2022-01-05    5     1

通过DataFrame.asfreq 添加了缺失的连续日期:

print (df.groupby('date')['numbers']
         .agg(['sum','size'])
         .asfreq('d', fill_value=0))
            sum  size
date                 
2022-01-01    6     3
2022-01-02    0     0
2022-01-03   11     2
2022-01-04    0     0
2022-01-05    5     1

每 3 天滚动使用 sum

df1 = (df.groupby('date')['numbers']
         .agg(['sum','size'])
         .asfreq('d', fill_value=0)
         .rolling(window=3, min_periods=1)
         .sum())
print (df1)
             sum  size
date                  
2022-01-01   6.0   3.0
2022-01-02   6.0   3.0
2022-01-03  17.0   5.0
2022-01-04  11.0   2.0
2022-01-05  16.0   3.0

df1 的列除以求平均值:

print (df1['sum'].div(df1['size']))
date
2022-01-01    2.000000
2022-01-02    2.000000
2022-01-03    3.400000
2022-01-04    5.500000
2022-01-05    5.333333
Freq: D, dtype: float64

排除 Series.shift 一天的一天:

print (df1['sum'].div(df1['size']).shift())
date
2022-01-01    NaN
2022-01-02    2.0
2022-01-03    2.0
2022-01-04    3.4
2022-01-05    5.5
Freq: D, dtype: float64

最后用于新列使用Series.map

print (df['date'].map(df1['sum'].div(df1['size']).shift()))
0    NaN
1    NaN
2    NaN
3    2.0
4    2.0
5    5.5
Name: date, dtype: float64

【讨论】:

  • 每个步骤在做什么的任何 sudo 代码解释?对于未来的读者? @jezrael
  • 嘿@jezrael - 如果我想扩展这个问题并最初将两件事分组 - 说日期和星期几 - 然后将其映射回原始 df。我该怎么做?
  • 如果您愿意,我可以将其变成一个新问题...
【解决方案2】:
col1=df1.groupby('date').date.agg(lambda ss:pd.date_range(ss.iloc[0]-pd.Timedelta(days=3),ss.iloc[0])).shift(-1).rename('col1')

df11=df1.set_index('date').join(col1).assign(col2=lambda dd:dd.apply(lambda ss:ss.name in ss.col1 if ss.col1 else None,axis=1).fillna(True)).query("col2").groupby(level=0)
    .numbers.mean().shift().rename('av')

df1.set_index('date').join(df11)


         numbers   av
date                    
2022-01-01        1  NaN
2022-01-01        2  NaN
2022-01-01        3  NaN
2022-01-03        4  2.0
2022-01-03        7  2.0
2022-01-05        5  5.5

【讨论】:

    猜你喜欢
    • 2020-07-04
    • 1970-01-01
    • 2018-05-23
    • 2018-05-11
    • 2018-10-03
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    • 2021-12-11
    相关资源
    最近更新 更多