簡體   English   中英

在傳遞給 apply() 的自定義函數中訪問先前計算的結果

[英]Access the result of a previous calculation in custom function passed to apply()

我正在 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()但即使滾動我也無法獲得先前的結果,只能獲得輸入系列的先前值。

最特別的類型你描述的操作中的可作為cummaxcummincumprodcumsumf(x) = x + f(x-1)

expanding對象中可以找到更多功能:均值、標准差、方差峰度、偏度、相關性等。

對於最一般的情況,您可以使用帶有自定義函數的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)

我列出的方法經過優化並且運行得非常快,但是當您使用自定義函數時,性能會變得更糟。 對於指數平滑,對於長度為 1000 的系列,pandas 的性能開始優於循環,但是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

類似的方法cummincumsum進行了優化,只需要X的當前值和功能的前值。 但是對於自定義函數,復雜度是O(n**2) 這主要是因為會有函數的先前值和x的當前值不足以計算函數的當前值的情況。 對於 cumsum,您可以使用先前的 cumsum 並添加當前值以得出結果。 比如說,幾何平均值,你不能這樣做。 這就是為什么即使是中等大小的系列也無法expanding

一般來說,迭代一個系列並不是一個非常昂貴的操作。 對於 DataFrames,它需要返回每一行的副本,因此效率非常低,但對於 Series 而言並非如此。 當然,您應該在可用時使用矢量化方法,但如果不是這種情況,則將 for 循環用於諸如遞歸計算之類的任務是可以的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM