在Pandas中使用命名滚动聚合自定义函数

3

我找不到一种正确命名应用于滚动窗口的自定义聚合函数的方法。这个回答解释了对于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'

我目前的做法是将一个包含“列: 函数列表”的字典传递给聚合函数,但在该情况下,生成的列将被命名。
('time', '<lambda>'),
('time', '<lambda>'), 

不幸的是,这并没有为列提供唯一值的索引对象。

总之我的问题是,如何为滚动窗口创建自定义函数的命名聚合?


这个能用吗?df[['time_mean', 'time_std']] = df.time.rolling(...).agg(['mean', 'std']]) 对于滚动聚合,命名聚合不起作用。 - Emma
很遗憾,我需要将特定的自定义函数应用于滚动窗口。 - Grinjero
你可以使用以下代码:df[['time_mean', 'time_std']] = df.time.rolling(...).agg([lambda w: window_daily_stats(w, np.mean), lambda w: window_daily_stats(w, np.std)])。你可以在列表中传递函数或函数名称(字符串)。 - Emma
3个回答

3

如果我理解正确,使用下划线双下划线属性'name'可以为lambda函数命名:

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

2
在撰写本文时,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并且特定于具体情况,因此留给读者作为练习。


0

使用pandas不可能实现这个功能

这种方法仅适用于数据框系列

此外,滚动窗口没有列标签可用于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)

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