尊重时间的pandas.groupby对象的移动平均值

时间:2018-12-04 08:56:13

标签: python pandas moving-average

给出以下格式的熊猫数据框:

toy = pd.DataFrame({
'id': [1,2,3,
       1,2,3,
       1,2,3],
'date': ['2015-05-13', '2015-05-13', '2015-05-13', 
         '2016-02-12', '2016-02-12', '2016-02-12', 
         '2018-07-23', '2018-07-23', '2018-07-23'],
'my_metric': [395, 634, 165, 
              144, 305, 293, 
              23, 395, 242]
})
# Make sure 'date' has datetime format
toy.date = pd.to_datetime(toy.date)

enter image description here

my_metric列包含一些(随机)量度,我希望以id列为条件来计算时间的移动平均值 并在指定的时间间隔内指定自己。我将此时间间隔称为“回溯时间”;这可能是5分钟 或2年。为了确定哪些观测值将包含在回溯计算中,我们使用date列(如果愿意,可以是索引)。

令我沮丧的是,我发现使用pandas内置函数不容易执行这样的过程,因为我需要有条件地执行计算 在id上进行,并且同时只能根据回溯时间内的观测值进行计算(使用date列进行检查)。因此,对于每个id-date组合,输出数据帧应由一行组成,其中my_metric列现在是包含在回溯时间内的所有观测值的平均值(例如2年,包括今天的日期。

为清楚起见,在使用2年回溯时间时,我提供了具有所需输出格式的图形(对超大图形表示歉意):

Desired result using a 2 year lookback time

我有一个解决方案,但是它没有利用特定的熊猫内置函数,并且可能不是最优的(列表理解和单个for循环的组合)。我正在寻找的解决方案不会使用for循环,因此更具可伸缩性/效率/更快。

谢谢!

2 个答案:

答案 0 :(得分:0)

计算回溯时间:(当前年份-2年)

from dateutil.relativedelta import relativedelta
from dateutil import parser
import datetime

In [1691]: dt = '2018-01-01'

In [1695]: dt = parser.parse(dt)

In [1696]: lookback_time = dt - relativedelta(years=2)

现在,根据回溯时间过滤数据框并计算滚动平均值

In [1722]: toy['new_metric'] = ((toy.my_metric + toy[toy.date > lookback_time].groupby('id')['my_metric'].shift(1))/2).fillna(toy.my_metric)

In [1674]: toy.sort_values('id')
Out[1674]: 
        date  id  my_metric  new_metric
0 2015-05-13   1        395       395.0
3 2016-02-12   1        144       144.0
6 2018-07-23   1         23        83.5
1 2015-05-13   2        634       634.0
4 2016-02-12   2        305       305.0
7 2018-07-23   2        395       350.0
2 2015-05-13   3        165       165.0
5 2016-02-12   3        293       293.0
8 2018-07-23   3        242       267.5

答案 1 :(得分:0)

因此,经过一番修补,我找到了一个可以充分概括的答案。我使用了一个略有不同的“玩具”数据框(与我的情况稍相关)。为了完整起见,以下是数据:

enter image description here

现在考虑以下代码:

# Define a custom function which groups by time (using the index)
def rolling_average(x, dt):
    xt = x.sort_index().groupby(lambda x: x.time()).rolling(window=dt).mean()
    xt.index = xt.index.droplevel(0)
    return xt

dt='730D' # rolling average window: 730 days = 2 years

# Group by the 'id' column
g = toy.groupby('id')

# Apply the custom function
df = g.apply(rolling_average, dt=dt)

# Massage the data to appropriate format
df.index = df.index.droplevel(0)
df = df.reset_index().drop_duplicates(keep='last', subset=['id', 'date'])

结果符合预期:

enter image description here