我有不同月份的不同组/类别的时间序列数据。在每个组中,我想计算当前月和前一个月(如果存在的话)的支出列的滚动平均值。
我正在重用这篇文章中的一些数据
import pandas as pd
from io import StringIO
data = StringIO(
"""
date spendings category
2020-01-01 10 A
2020-01-01 20 A
2020-01-01 15 A
2020-02-01 10 B
2020-02-01 10 B
2020-02-01 14 A
2020-02-01 19 A
2020-03-01 50 A
2020-04-01 40 A
"""
)
df = pd.read_csv(data,sep="s+",parse_dates=True,index_col="date")
因此,对于上面的示例数据,对于A类,2020-02-01月份的平均值应为(10+20+15+14+19)/5。对于2020-03-01月份,A类的平均值为(14+19+50)/3。
这是我迄今为止使用Panda的rolling()函数尝试的一种方法:
df = df.sort_index()
df.groupby('category').rolling('30D').spendings.mean()
和结果:
category date
A 2020-01-01 10.000000
2020-01-01 15.000000
2020-01-01 15.000000
2020-02-01 14.000000
2020-02-01 16.500000
2020-03-01 27.666667
2020-04-01 40.000000
B 2020-02-01 10.000000
2020-02-01 10.000000
我们可以看到有两个问题。
- 当前逻辑取当前行的值和30天内所有先前行的值的平均值,但不包括同月其他行的值。
- 30D参数不等于1个月。我们可以看到,对于A类,3月份有3月和2月的平均值,但4月份在计算平均值时不包括2020-03-01的值。
其他一些Stackoverflow帖子提到使用月或月开始作为滚动频率而不是30D,但我现在的主要挑战是弄清楚如何在计算平均值时捕获当前月份和前一个月的所有值。
编辑:预期的输出
category date
A 2020-01-01 15.000000
2020-02-01 15.600000
2020-03-01 27.666667
2020-04-01 45.000000
B 2020-02-01 10.000000
找到了一个使用tail()和玩弄天数的解决方案
我相信肯定有更好的解决方案。但由于它不是一个真正的滚动平均值,我认为你不能正确地使用滚动计算。
下面的解决方案有效。您可以将pd.DateOffset(day=1)
添加到偏移日期中,以便也考虑天数。
df = pd.read_csv(data,sep="s+")
df['date'] = pd.to_datetime(df['date']) #easier to use pd datetimes
# get average spendings in current and previous month
def get_average(row):
current_month = row.date
previous_month = current_month - pd.DateOffset(months=1)
#create filter for rows with same category and same or previous month
date_category_filter = (((df.category == row.category) & (df.date == current_month)) | ((df.category == row.category) & (df.date == previous_month)))
#return mean of all filtered rows
return df.loc[date_category_filter]['spendings'].mean()
df['moving average'] = df.apply(get_average, axis=1)
# function returns values for all rows, hence contains duplicates.
# group data and use min to remove duplicates
df.groupby(['category','date']).min()