pandas.groupby 尊重时间的对象的移动平均线
Moving average on pandas.groupby object that respects time
给定一个 pandas 数据帧,格式如下:
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)
my_metric
列包含一些(随机)指标,我希望以列 id
为条件计算随时间变化的移动平均值
并在我自己指定的某个指定时间间隔内。我将此时间间隔称为 "lookback time";这可能是 5 分钟
或 2 年。为了确定要包含在回顾计算中的观察结果,我们使用 date
列(如果您愿意,可以是索引)。
令我沮丧的是,我发现使用 pandas 内置函数不容易执行这样的过程,因为我需要有条件地执行计算
在 id
上,同时应仅对回顾时间内的观察结果进行计算(使用 date
列进行检查)。因此,输出数据帧应由每个 id
-date
组合的一行组成,my_metric
列现在是回顾时间内包含的所有观察值的平均值(例如 2年,包括今天的日期)。
为清楚起见,在使用 2 年回溯时间时,我提供了一个具有所需输出格式的图表(为过大的图表道歉):
我有一个解决方案,但它没有使用特定的 pandas 内置函数,并且可能不是最优的(列表理解和单个 for 循环的组合)。我正在寻找的解决方案不会使用 for 循环,因此更 scalable/efficient/fast.
谢谢!
计算回顾时间:(Current_year - 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
因此,经过一番修改后,我找到了一个可以充分概括的答案。我使用了一个略有不同的 'toy' 数据框(与我的案例更相关)。为了完整起见,这里是数据:
现在考虑以下代码:
# 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'])
结果符合预期:
给定一个 pandas 数据帧,格式如下:
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)
my_metric
列包含一些(随机)指标,我希望以列 id
为条件计算随时间变化的移动平均值
并在我自己指定的某个指定时间间隔内。我将此时间间隔称为 "lookback time";这可能是 5 分钟
或 2 年。为了确定要包含在回顾计算中的观察结果,我们使用 date
列(如果您愿意,可以是索引)。
令我沮丧的是,我发现使用 pandas 内置函数不容易执行这样的过程,因为我需要有条件地执行计算
在 id
上,同时应仅对回顾时间内的观察结果进行计算(使用 date
列进行检查)。因此,输出数据帧应由每个 id
-date
组合的一行组成,my_metric
列现在是回顾时间内包含的所有观察值的平均值(例如 2年,包括今天的日期)。
为清楚起见,在使用 2 年回溯时间时,我提供了一个具有所需输出格式的图表(为过大的图表道歉):
我有一个解决方案,但它没有使用特定的 pandas 内置函数,并且可能不是最优的(列表理解和单个 for 循环的组合)。我正在寻找的解决方案不会使用 for 循环,因此更 scalable/efficient/fast.
谢谢!
计算回顾时间:(Current_year - 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
因此,经过一番修改后,我找到了一个可以充分概括的答案。我使用了一个略有不同的 'toy' 数据框(与我的案例更相关)。为了完整起见,这里是数据:
现在考虑以下代码:
# 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'])
结果符合预期: