我正在使用Python处理Pandas,在将自定义函数应用于序列时,我想访问先前计算的结果。
大致像这样:
import pandas # How can I obtain previous_result? def foo(value, previous_result = None): # On the first iteration there is no previous result if previous_result is None: previous_result = value return value + previous_result series = pandas.Series([1,2,3]) print(series.apply(foo))
这也可以概括为“如何将n以前的结果传递给函数?”。我知道,series.rolling()但是即使滚动,我也无法获得先前的 结果 ,只能获得输入序列的先前值。
n
series.rolling()
最特别的类型你所描述的操作都可以作为cummax,cummin,cumprod 和cumsum(f(x) = x + f(x-1))。
cummax
cummin
cumprod
cumsum
f(x) = x + f(x-1)
可以在expanding对象中找到更多功能:平均值,标准偏差,方差峰度,偏度,相关性等。
expanding
在大多数情况下,您可以使用expanding().apply() 自定义函数。例如,
expanding().apply()
from functools import reduce # For Python 3.x ser.expanding().apply(lambda r: reduce(lambda prev, value: prev + 2*value, r))
相当于 f(x) = 2x + f(x-1)
f(x) = 2x + f(x-1)
我列出的方法已经过优化,并且运行速度非常快,但是当您使用自定义函数时,性能会变差。为了进行指数平滑,对于长度为1000的Series,熊猫开始表现出比循环更好expanding().apply()的性能,但是reduce的性能却很糟糕:
np.random.seed(0) ser = pd.Series(70 + 5*np.random.randn(10**4)) ser.tail() Out: 9995 60.953592 9996 70.211794 9997 72.584361 9998 69.835397 9999 76.490557 dtype: float64 ser.ewm(alpha=0.1, adjust=False).mean().tail() Out: 9995 69.871614 9996 69.905632 9997 70.173505 9998 70.139694 9999 70.774781 dtype: float64 %timeit ser.ewm(alpha=0.1, adjust=False).mean() 1000 loops, best of 3: 779 µs per loop
带循环:
def exp_smoothing(ser, alpha=0.1): prev = ser[0] res = [prev] for cur in ser[1:]: prev = alpha*cur + (1-alpha)*prev res.append(prev) return pd.Series(res, index=ser.index) exp_smoothing(ser).tail() Out: 9995 69.871614 9996 69.905632 9997 70.173505 9998 70.139694 9999 70.774781 dtype: float64 %timeit exp_smoothing(ser) 100 loops, best of 3: 3.54 ms per loop
总时间仍以毫秒为单位,但是expanding().apply():
ser.expanding().apply(lambda r: reduce(lambda p, v: 0.9*p+0.1*v, r)).tail() Out: 9995 69.871614 9996 69.905632 9997 70.173505 9998 70.139694 9999 70.774781 dtype: float64 %timeit ser.expanding().apply(lambda r: reduce(lambda p, v: 0.9*p+0.1*v, r)) 1 loop, best of 3: 13 s per loop
类似的方法cummin,cumsum进行了优化,只需要X的当前值和功能的前值。但是,使用自定义功能时,复杂度为O(n**2)。这主要是因为在某些情况下,函数的先前值和x的当前值不足以计算函数的当前值。对于累积量,您可以使用以前的累积量并添加当前值以得出结果。您不能这样做,例如说几何均值。这就是为什么expanding即使中等大小的Series也无法使用的原因。
O(n**2)
通常,对Series进行迭代并不是很昂贵的操作。对于DataFrames,它需要返回每一行的副本,因此效率非常低,但是Series并非如此。当然,应该在可用的情况下使用向量化方法,但是如果不是这种情况,则对诸如递归计算之类的任务使用for循环是可以的。