为何Pandas字符串Series调用rolling.apply未执行自定义函数?
为什么Object类型Series的rolling.apply没执行你的自定义函数?
这其实是pandas针对不同数据类型的rolling.apply做的特殊优化逻辑导致的,不是说object类型列不能用rolling方法——咱们一步步拆解:
首先看你的测试:数值型Series用rolling.apply(myfunc)会立刻抛出异常,说明函数确实被逐窗口执行了;但换成字符串(object类型)的Series,不管直接传myfunc还是用lambda包装,都没反应,连异常都不抛,这明显是函数根本没被调用。
核心原因:raw参数的默认行为+Object类型的特殊处理
pandas的rolling.apply有个raw参数,默认情况下对于Series是True——意思是把每个窗口的数据以numpy数组的形式传递给自定义函数。
- 对于数值型Series:numpy数组可以正常处理,pandas会老老实实逐窗口调用你的
myfunc,所以异常会触发。 - 对于Object类型Series:当
raw=True时,窗口是一个object类型的numpy数组。pandas内部会判断:这种类型的数组没法执行通用的自定义函数(毕竟object元素可能是任意类型,没有统一运算规则),于是直接跳过了函数执行,直接返回一个全NaN的Series——这就是为什么你看不到异常的原因,函数根本没跑起来。
验证一下?
咱们给myfunc加个打印语句试试:
import pandas as pd def myfunc(x): print("我被调用啦!") raise Exception() # 数值型:会打印+抛异常 pd.Series([1,2,3,4]).rolling(2).apply(myfunc) # Object型:啥都不打印,直接返回全NaN pd.Series(["A","A","B","B"]).rolling(2).apply(myfunc)
确实,object类型的情况连打印都没有,说明函数没被执行。
怎么让Object类型的rolling.apply执行自定义函数?
很简单,显式设置raw=False,强制pandas把每个窗口包装成pandas Series对象传递给函数,这时候pandas就会逐窗口执行你的函数了:
pd.Series(["A", "A", "B", "B", "B", "B", "B", "B", "A", "A", "A"]).rolling(3).apply(myfunc, raw=False)
运行这段代码,你会立刻看到抛出的异常,证明myfunc被调用了。
总结一下
- Object类型列完全可以用rolling方法,只是
rolling.apply的默认行为会跳过自定义函数执行; - 根源是pandas对object类型的raw模式窗口做了优化,直接返回NaN而不执行函数;
- 只要设置
raw=False,就能让自定义函数在object类型的rolling窗口中正常执行。
内容的提问来源于stack exchange,提问作者r0f1
相关产品推荐
相关产品推荐

