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

Posted

技术标签:

【中文标题】如何使用天数作为 pandas rolling_apply 函数的窗口【英文标题】:How to use days as window for pandas rolling_apply function 【发布时间】:2016-05-16 12:47:16 【问题描述】:

我有一个带有不规则日期间隔的 pandas 数据框。有没有办法使用 7 天作为移动窗口来计算median absolute deviation、中位数等?我觉得我可以以某种方式使用pandas.rolling_apply,但它不需要不规则间隔的日期作为窗口参数。我发现了一个类似的帖子https://***.com/a/30244019/3128336 并正在尝试创建我的自定义函数但仍然无法弄清楚.. 谁能帮忙?

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

【问题讨论】:

你想要移动窗口还是扩展窗口? 我要移动窗口。所以像pd.rolling_apply(df, window=relativedelta(days=7), func, min_periods=1) 这样的事情我在这两者之间感到困惑。让我更正我的帖子。谢谢指出 你能解释一下你所链接的问题的解决方案有什么不适用的吗?我想在滚动应用之前将您的数据重新采样为每日数据确实会删除重复的日期? 我认为我不应该使用pandas.resample 来填充之前的值,因为这个df 包含不同学生的考试成绩。这不像一家公司的股票价格如果是这样,我同意我可以用以前的值填充周末的 NA。 【参考方案1】:

您可以使用时间增量选择窗口中的行,然后使用 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

【讨论】:

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

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

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 类型的 cmets 表示歉意......它仍在进行中。

【讨论】:

【参考方案3】:

根据 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

【讨论】:

以上是关于如何使用天数作为 pandas rolling_apply 函数的窗口的主要内容,如果未能解决你的问题,请参考以下文章

Pandas - 如何根据自定义日历计算两个日期之间的天数[关闭]

Pandas - 计算分组天数

pandas使用date_range函数生成日期序列数据pandas将两个日期序列数据作差生成新的日期差数据列(8 days)并提取天数数值

Pandas 中一列中的值之间的平均天数差异

Pandas:时间戳系列中的唯一天数

pandas使用pd.DateOffset生成时间偏移量(指定年数月数天数小时分钟)把dataframe数据中的时间数据列统一偏移(相减偏移向前偏移时间减小)