【问题标题】:How to compute rolling average in pandas just for a specific date如何计算特定日期熊猫的滚动平均值
【发布时间】:2023-01-31 02:05:21
【问题描述】:

我在下面有这个示例数据框。我创建了一个函数来执行我想要的操作,为前一天的每个 Store 计算一个 Sales 滚动平均值(7.14 天窗口)并将其移动到当前日期。我怎么计算这个只要对于特定日期,例如2022-12-31?我有很多行,我不想每次添加日期时都重新计算它。

import numpy as np
import pandas as pd

ex = pd.DataFrame({'Date':pd.date_range('2022-10-01', '2022-12-31'), 
                   'Store': np.random.choice(2, len(pd.date_range('2022-10-01', '2022-12-31'))),
                   'Sales': np.random.choice(10000, len(pd.date_range('2022-10-01', '2022-12-31')))})

ex.sort_values(['Store','Date'], ascending=False, inplace=True)

for days in [7, 14]:
    ex['Sales_mean_' + str(days) + '_days'] = ex.groupby('Store')[['Sales']].apply(lambda x: x.shift(-1).rolling(days).mean().shift(-days+1))```

【问题讨论】:

    标签: python pandas dataframe rolling-computation


    【解决方案1】:

    我重新定义了一个类似的数据框,因为使用随机变量生成器会使调试变得困难。在每次测试中,数据帧随机变化。

    为了简单起见,我将使用 2 和 3 个移动平均线周期。

    起始数据框

            Date  Store  Sales
    9 2022-10-10      1   5347
    8 2022-10-09      1   1561
    7 2022-10-08      1   5648
    6 2022-10-07      1   8123
    5 2022-10-06      1   1401
    4 2022-10-05      0   2745
    3 2022-10-04      0   7848
    2 2022-10-03      0   3151
    1 2022-10-02      0   4296
    0 2022-10-01      0   9028
    

    它给 :

    ex = pd.DataFrame({
      "Date": pd.date_range('2022-10-01', '2022-10-10'),
      "Store": [0]*5+[1]*5,
      "Sales": [9028, 4296, 3151, 7848, 2745, 1401, 8123, 5648, 1561, 5347],
    })
    
    ex.sort_values(['Store','Date'], ascending=False, inplace=True)
    

    建议代码

    import pandas as pd
    import numpy as np
    
    ex = pd.DataFrame({
      "Date": pd.date_range('2022-10-01', '2022-10-10'),
      "Store": [0]*5+[1]*5,
      "Sales": [9028, 4296, 3151, 7848, 2745, 1401, 8123, 5648, 1561, 5347],
    })
    
    ex.sort_values(['Store','Date'], ascending=False, inplace=True)
    
    periods=(2,3)
    
    ### STEP 1 -- Initialization : exhaustive Mean() Calculation
    for per in periods:
        ex["Sales_mean_{0}_days".format(per)] = (
            ex.groupby(['Store'])['Sales']
              .apply(lambda g: g.shift(-1)
                                .rolling(per)
                                .mean()
                                .shift(-per+1))
         )
    
    ### STEP 2 -- New Row Insertion
    def fmt_newRow(g, newRow, periods):
        return {
          "Date": pd.Timestamp(newRow[0]),
          "Store": newRow[1],
          "Sales": newRow[2],
          "Sales_mean_{0}_days".format(periods[0]): (g['Sales'].iloc[0:periods[0]].sum()) / periods[0],
          "Sales_mean_{0}_days".format(periods[1]): (g['Sales'].iloc[0:periods[1]].sum()) / periods[1],
        }    
    
    def add2DF(ex, newRow):
        # g : sub-Store group
        g = (
            ex.loc[ex.Store==newRow[1]]
              .sort_values(['Store','Date'], ascending=False)
        )
        # Append newRow like a dictionnary and sort by ['Store','Date']
        ex = (
            ex.append(fmt_newRow(g, newRow, periods), ignore_index=True)
              .sort_values(['Store','Date'], ascending=False)
              .reset_index(drop=True)
        )
        #
        return ex
    
    newRow = ['2022-10-11', 1, 2803] # [Date, Store, Sales]
    ex = add2DF(ex, newRow)
    
    print(ex)
    

    结果

             Date  Store  Sales  Sales_mean_2_days  Sales_mean_3_days
    0  2022-10-11      1   2803             3454.0        4185.333333
    1  2022-10-10      1   5347             3604.5        5110.666667
    2  2022-10-09      1   1561             6885.5        5057.333333
    3  2022-10-08      1   5648             4762.0                NaN
    4  2022-10-07      1   8123                NaN                NaN
    5  2022-10-06      1   1401                NaN                NaN
    6  2022-10-05      0   2745             5499.5        5098.333333
    7  2022-10-04      0   7848             3723.5        5491.666667
    8  2022-10-03      0   3151             6662.0                NaN
    9  2022-10-02      0   4296                NaN                NaN
    10 2022-10-01      0   9028                NaN                NaN
    

    注释

    • 新行是这样一个列表:[Date, Store, Sales]
    • 每次需要将新行保存到数据框时,将其传递给fmt_newRow函数和相应的子组g
    • fmt_newRow 以字典的形式返回一个新行,该行与append Pandas 函数集成在数据框中
    • 无需重新计算所有平均值,因为只有 per-last g 值用于计算新的行平均值
    • 第 2 期和第 3 期的移动平均线已经过检查并且是正确的。

    【讨论】:

    • 逻辑看起来不错,但是添加行时的计算没有给出正确的结果。例如,对于商店 1 的 2022-10-11Sales_mean_2_days 应为 (5347+1561)/2 = 3604.5,与上一行相同:(5648+1561)/2。我更改了下面的代码,它给出了正确的意思。
    • "Sales_mean_{0}_days".format(periods[0]): g['Sales'].iloc[1:periods[0]+1].mean(), "Sales_mean_{0}_days".format(periods[1]): g['Sales'].iloc[1:periods[1]+1].mean(),
    • 很抱歉这个错误,我在火车上编码。我认为您的回答存在一点概念上的偏见。我从零开始更正:(g['Sales'].iloc[0:periods[0]].sum()) / ...。因为比较总是取决于最后一个值的平均值,不包括实际值
    猜你喜欢
    • 2019-11-24
    • 1970-01-01
    • 1970-01-01
    • 2019-11-19
    • 2017-09-12
    • 2019-04-16
    • 1970-01-01
    • 2018-08-29
    相关资源
    最近更新 更多