在Pandas DataFrame中应用带参自定义滚动函数报错求助
问题描述
现有如下DataFrame(df.head()结果):
date colA 0 2018-01-05 0.6191 1 2018-01-20 0.5645 2 2018-01-25 0.5641 3 2018-01-27 0.5404 4 2018-01-30 0.4933
需求是对每连续3行递归应用自定义函数,即依次处理行0-2、1-3、2-4...。
编写的代码如下:
def my_rolling_func(df, val): p1 = (df['date']-df['date'].min()).dt.days.tolist()[0],df[val].tolist()[0] p2 = (df['date']-df['date'].min()).dt.days.tolist()[1],df[val].tolist()[1] p3 = (df['date']-df['date'].min()).dt.days.tolist()[2],df[val].tolist()[2] return sum([i*j for i,j in [p1,p2,p3]]) df.rolling(3,center=False,axis=1).apply(my_rolling_func, args=('colA'))
运行后出现错误:
ValueError: Length of passed values is 1, index implies 494.
其中494是DataFrame的总行数,不清楚错误原因,原以为滚动窗口会按定义的大小(3)生成DataFrame切片,再对该子集应用函数。
错误原因分析
axis参数方向错误:设置axis=1表示按列滚动窗口,但需求是按行方向处理连续3行,应设置axis=0(默认值可省略)。args参数格式错误:args要求传入元组,('colA')是单个字符串而非元组,正确写法为('colA',)。rolling.apply的参数限制:对DataFrame使用rolling.apply时,默认传递的是窗口内的Series而非完整DataFrame切片,且在函数内无法直接使用dt访问器(滚动窗口对象不支持该操作)。- 函数逻辑冗余:手动提取p1/p2/p3的写法依赖固定窗口大小,且重复计算
dt.days,效率低下。
修正后的解决方案
步骤1:预处理日期列
先将日期转换为可计算的数值格式,避免在滚动函数中处理日期类型:
import pandas as pd # 确保date列为datetime类型 df['date'] = pd.to_datetime(df['date']) # 计算每行日期相对于全局起始日期的天数 df['days'] = (df['date'] - df['date'].min()).dt.days
步骤2:调整函数与滚动调用
修改函数以适配滚动窗口的参数传递逻辑,同时修正rolling的参数设置:
def my_rolling_func(window_df): # 提取窗口内的天数和colA值 days = window_df['days'].values colA = window_df['colA'].values # 计算窗口内日期相对于窗口第一天的天数 rel_days = days - days[0] # 返回乘积和 return sum(rel_days * colA) # 使用raw=False让滚动窗口传递完整的DataFrame切片 df['result'] = df.rolling(3, raw=False).apply(my_rolling_func)
简化版(若使用全局起始天数计算)
如果不需要窗口内的相对天数,直接用全局起始日期的数值计算:
def my_rolling_func(window_df): days = window_df['days'].values colA = window_df['colA'].values return sum(days * colA) df['result'] = df.rolling(3, raw=False).apply(my_rolling_func)
高性能版本(避免DataFrame切片传递)
若数据量较大,可提前将需要的列转为二维数组,用raw=True提升性能:
# 准备二维数组:每行对应[days, colA] data = df[['days', 'colA']].values def my_rolling_func(arr): # 将展平的数组重新转为3行2列的结构 arr = arr.reshape(-1, 2) rel_days = arr[:,0] - arr[0,0] return sum(rel_days * arr[:,1]) df['result'] = pd.Series(data.tolist()).rolling(3).apply(my_rolling_func, raw=True)
验证结果
以前3行数据为例:
- 全局天数为0、15、20,窗口内相对天数为0、15、20
- 乘积和为
0*0.6191 + 15*0.5645 + 20*0.5641 = 19.7495
对应df['result'][2]的值即为19.7495,符合预期逻辑。
内容的提问来源于stack exchange,提问作者user88484
相关产品推荐
相关产品推荐

