我无法找到一种方法来正确命名应用于滚动窗口的自定义聚合函数。 这个答案很好地解释了
groupby
聚合。我尝试过使用pd.NamedAggregates
,就像这样
df
.rolling(f"{num_days_window + 1}D", min_periods=day_length)
.aggregate(time_mean=pd.NamedAgg(column="time", aggfunc=lambda w: window_daily_stats(w, np.mean)),
time_std=pd.NamedAgg(column="time", aggfunc=lambda w: window_daily_stats(w, np.std)))
用于命名的嵌套字典已被弃用,因此这不是一个选项。传递元组也不起作用。
.rolling(f"{num_days_window + 1}D", min_periods=day_length)
.aggregate(time_mean=("time", lambda w: window_daily_stats(w, np.mean)),
time_std=("time", lambda w: window_daily_stats(w, np.std)))
在这两种情况下,错误是相同的:
TypeError: aggregate() missing 1 required positional argument: 'func'
我目前的做法是向聚合函数传递一个包含
column: list of functions
对的字典,但在这种情况下,结果列被命名为
('time', '<lambda>'),
('time', '<lambda>'),
不幸的是,这并没有为我提供唯一值的列索引对象。
总而言之,我的问题是,如何为滚动窗口的自定义函数创建命名聚合?
IIUC,有一种方法可以使用 lambda 函数的 dunder 属性“name”:
def window_daily_stats(w, function):
return function(w)
cust_mean = lambda s: window_daily_stats(s, np.mean)
cust_std = lambda s: window_daily_stats(s, np.std)
cust_mean.__name__ = 'custom mean'
cust_std.__name__ = 'custom std'
然后:
df.rolling(1).agg({'a':[cust_mean, cust_std]})
输出:
a
custom mean custom std
0 0.0 0.0
1 1.0 0.0
2 2.0 0.0
3 3.0 0.0
4 4.0 0.0
在撰写本文时,
pandas==1.5.3
不支持 NamedAgg
聚合的 .rolling
语法。最接近的方法是使用要应用的函数列表,然后应用自定义重命名。
请注意,
lambda
列是由于使用了匿名 lambda 函数,因此简单的修复方法是使用常规函数:
from pandas import DataFrame
df = DataFrame(zip(range(5), range(5)), columns=['a', 'b'])
# these will be anonymous
mean = lambda x: sum(x)/len(x)
summ = lambda x: sum(x)
def mmax(x):
return max(x)
def mmin(x):
return min(x)
agg = df.rolling(1).agg({'a': [mean, summ], 'b': [mmax, mmin]})
print(agg)
# a b
# <lambda> <lambda> mmax mmin
# 0 0.0 0.0 0.0 0.0
# 1 1.0 1.0 1.0 1.0
# 2 2.0 2.0 2.0 2.0
# 3 3.0 3.0 3.0 3.0
# 4 4.0 4.0 4.0 4.0
最后,为了拥有自定义重命名逻辑,我们可以通过执行重命名的函数来传输数据帧:
def _rename(df):
df = df.copy() # avoid mutating the original
df.columns = ["".join(c) for c in df.columns] # can apply custom renaming logic
return df
print(agg.pipe(_rename))
# a<lambda> a<lambda> bmmax bmmin
# 0 0.0 0.0 0.0 0.0
# 1 1.0 1.0 1.0 1.0
# 2 2.0 2.0 2.0 2.0
# 3 3.0 3.0 3.0 3.0
# 4 4.0 4.0 4.0 4.0
原则上,
_rename
可以通过编程方式从为命名聚合准备的现有字典中构建。准备它是纯 Python 的,并且是针对具体情况的,所以它留给读者作为练习。
此方法仅适用于dataframes和series。
此外,滚动窗口没有可与
pd.NamedAgg
一起使用的列标签。
随着滚动窗口的进展,将滚动窗口发送到聚合函数以通过使用
pd.NamedAgg
规范将计算结果放置在新列中来对其应用多个函数的概念当前不存在。
所以我们必须另辟蹊径才能达到预期的效果。
替代方案
assign
import pandas as pd
df = pd.DataFrame({'col1':[1, 1, 2, 3, 3, 5, 8],
'col2':[1, 1, 2, 3, 3, 5, 8]})
df = (df.assign(special_name=df.rolling(3).aggregate({'col2': 'sum'}))
.drop('col2', axis=1)
)
# col1 special_name
# 0 1 NaN
# 1 1 NaN
# 2 2 4.0
# 3 3 6.0
# 4 3 8.0
# 5 5 11.0
# 6 8 16.0
我们
assign
首先一个新的列名称,这个新列将收到最终结果系列。
请注意,我们添加
.drop('col2', axis=1)
来删除数据源 col2
。
还可以使用
assign
在滚动窗口上使用多个函数,如以下脚本所示:
import pandas as pd
df = pd.DataFrame({'col1':[1, 1, 2, 3, 3, 5, 8],
'col2':[1, 1, 2, 3, 3, 5, 8]})
def sum_square(x):
return sum([e**2 for e in x])
roller = df.rolling(3)
df.assign(
special_name = roller.aggregate({'col2': 'sum'}),
special_name2 = roller.aggregate({'col2': 'mean'}),
special_name3 = roller.aggregate({'col2': lambda s: sum_square(s)})
)
col1 col2 special_name special_name2 special_name3
0 1 1 NaN NaN NaN
1 1 1 NaN NaN NaN
2 2 2 4.0 1.333333 2.0
3 3 3 6.0 2.000000 3.0
4 3 3 8.0 2.666667 3.0
5 5 5 11.0 3.666667 5.0
6 8 8 16.0 5.333333 8.0
请注意,如果我们使用多个数据源,则可以在计算操作结束时删除它们,例如使用以下指令:
df.drop(['col1', 'col2', ..., 'coln'], axis=1, inplace=True)