如何将天数作为pandas的rolling_apply函数窗口使用

9
我有一个带有不规则日期的pandas数据框。是否有办法使用7天作为移动窗口来计算中位数绝对偏差,中位数等?我觉得我可以在某种程度上使用pandas.rolling_apply,但它不会将不规则的日期作为窗口参数。我找到了一个类似的帖子https://dev59.com/KmUo5IYBdhLWcg3wxB7T#30244019 ,并尝试创建自定义函数,但仍然无法解决..请问有人能帮忙吗?
import pandas as pd
from datetime import datetime

person = ['A','B','C','B','A','C','A','B','C','A',]
ts = [
    datetime(2000, 1, 1),
    datetime(2000, 1, 1),
    datetime(2000, 1, 10),
    datetime(2000, 1, 20),
    datetime(2000, 1, 25),
    datetime(2000, 1, 30),
    datetime(2000, 2, 8),
    datetime(2000, 2, 12),
    datetime(2000, 2, 17),
    datetime(2000, 2, 20),
]
score = [9,2,1,3,8,4,2,3,1,9]
df = pd.DataFrame({'ts': ts, 'person': person, 'score': score})

df看起来是这样的

    person  score   ts
0   A       9       2000-01-01
1   B       2       2000-01-01
2   C       1       2000-01-10
3   B       3       2000-01-20
4   A       8       2000-01-25
5   C       4       2000-01-30
6   A       2       2000-02-08
7   B       3       2000-02-12
8   C       1       2000-02-17
9   A       9       2000-02-20

你想要一个移动窗口还是一个扩展窗口? - Stop harming Monica
我想要一个移动窗口。就像这样 pd.rolling_apply(df, window=relativedelta(days=7), func, min_periods=1) 我之前有些困惑,现在我已经更正了我的帖子。谢谢你指出来。 - E.K.
你能解释一下你所链接的问题的解决方案有什么问题吗?我猜在执行滚动应用之前将数据重新采样为每日数据是否会删除重复日期? - joris
我认为我不应该使用 pandas.resample 来填充先前的值,因为这个数据框包含不同学生的测试成绩。它不像一个公司的股票价格。如果是这样,我同意可以使用先前的值来填充周末的缺失值。 - E.K.
3个回答

5
您可以使用时间差来选择窗口内的行,然后使用apply运行每一行并进行聚合操作:
>>> from datetime import timedelta
>>> delta = timedelta(days=7)
>>> df_score_mean = df.apply(lambda x: np.mean(df['score'][df['ts'] <= x['ts'] + delta]), axis=1)
0    5.500000
1    5.500000
2    4.000000
3    4.600000
4    4.500000
5    4.500000
6    4.555556
7    4.200000
8    4.200000
9    4.200000

这已经接近我所需要的了!只有一个问题,如何改变apply中shift的工作方式?你建议的lambda函数是相反的。例如,pd.rolling_median(df.score, window=2)对于第一行返回NA,而不是最后一行。(实际上,我想添加一个等同于min_periods = 1的功能,以复制第一周的值) - E.K.
1
我认为lambda函数不应该对任何行返回NA,因为它总是会选择至少一行来执行np.mean()函数。你是在问如何将窗口改为向前或向后看吗?在lambda函数中,我们选择任何小于等于当前行+7天的行。如果您想向后查看7天,可以选择大于等于当前行-7天的行。 - Brian Huey
啊,这很有道理!是的,我需要向后查找。 - E.K.

0

我对滚动日期函数不够熟悉,所以我想着是否可以添加缺失的数据(实际上是一个充满缺失数据的数据框),然后你的滚动窗口应该更容易实现。

from datetime import date
import pandas as pd
##############Your Initial DataFrame ##############
person = ['A','B','C','B','A','C','A','B','C','A',]
ts = [
    datetime(2000, 1, 1),
    datetime(2000, 1, 1),
    datetime(2000, 1, 10),
    datetime(2000, 1, 20),
    datetime(2000, 1, 25),
    datetime(2000, 1, 30),
    datetime(2000, 2, 8),
    datetime(2000, 2, 12),
    datetime(2000, 2, 17),
    datetime(2000, 2, 15),
]
score = [9,2,1,3,8,4,2,3,1,9]
df = pd.DataFrame({'ts': ts, 'person': person, 'score': score})
################## Blank DataFrame in Same Format ###############
#Create some dates
start = date(2000,1,1)
end = date(2000,3,1)
#We have 3 people
Eperson=['A','B','C']
#They Score 0
Escore=[0]
#Need a date range in Days
ets=pd.date_range(start, end, freq='D')
dfEmpty=pd.DataFrame([(c,b,0) for b in Eperson for c in ets])
dfEmpty.columns=['ts','person','score']

################# Now Join them 

dfJoin=dfEmpty.merge(df,how='outer',on=['ts','person'])
dfJoin['score']=dfJoin.score_x+dfJoin.score_y
dfJoin.score.fillna(0,inplace=True)
del dfJoin['score_x']
del dfJoin['score_y']'

现在您拥有的数据框每个人都没有缺失日期 - 如果原始日期缺失,则该人/分数将为0。

我知道如果您处理数百万条记录,这可能不起作用。

对于非PEP类型的评论,我表示歉意... 这仍然是进展中的工作。


0

根据Brian Huey的suggestion,发表我的解决方案。

from datetime import datetime, timedelta
import statsmodels.api as sm

delta = timedelta(days=7)

def calc_mad_mean(row):
    start = row['ts']
    end = start + delta
    subset = df['score'][(start <= df['ts']) & (df['ts'] < end)]
    return pd.Series({'mad': sm.robust.mad(subset), 'med': np.median(subset)})

first_wk = df.ts.iloc[0] + delta
results = df[first_wk < df.ts].apply(calc_mad_mean, axis=1)
df.join(results, how='outer')

结果

    person  score   ts           mad        med
0   A       9       2000-01-01   NaN        NaN
1   B       2       2000-01-01   NaN        NaN
2   C       1       2000-01-10   0.000000   1.0
3   B       3       2000-01-20   3.706506   5.5
4   A       8       2000-01-25   2.965204   6.0
5   C       4       2000-01-30   0.000000   4.0
6   A       2       2000-02-08   0.741301   2.5
7   B       3       2000-02-12   1.482602   2.0
8   C       1       2000-02-17   5.930409   5.0
9   A       9       2000-02-20   0.000000   9.0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接