2015-10-23 24 views
4

與此問題類似Exponential Decay on Python Pandas DataFrame,我想快速計算數據幀中某些列的指數衰減總和。但是,數據幀中的行不是均勻分佈的。因此,雖然exponential_sum[i] = column_to_sum[i] + np.exp(-const*(time[i]-time[i-1])) * exponential_sum[i-1],權重np.exp(...)不會分解,我不明白如何改變這個問題,並仍然利用熊貓/ numpy矢量化。有沒有熊貓矢量化解決這個問題?熊貓:指數衰減和變量的和

爲了說明所需的計算,這裏是使用1:1的衰減常數存儲在SumA指數移動之和的樣本幀:

time A  Sum 
0 1.00 1 1.000000 
1 2.10 3 3.332871 
2 2.13 -1 2.234370 
3 3.70 7 7.464850 
4 10.00 2 2.013708 
5 10.20 1 2.648684 
+0

你可以重新取樣的數據幀? – maxymoo

+0

@Alexander我問的是總和,而不是平均值,雖然也許有一個明顯的變化 –

+0

@Alexander我更仔細地閱讀了這個問題,我不認爲它解決了我的問題,這是如何向量化numpy/pandas計算。我沒有任何問題在python循環中計算指數和,我只是在足夠大的框架上進行操作,這些框架能夠對計算進行矢量化處理。 –

回答

3

這個問題是複雜得多,它第一次出現。我最終使用numba的jit編譯生成器函數來計算指數和。我的最終結果是在我的計算機上計算一秒鐘內500萬行的指數總和,希望它足夠滿足您的需求。

# Initial dataframe. 
df = pd.DataFrame({'time': [1, 2.1, 2.13, 3.7, 10, 10.2], 
        'A': [1, 3, -1, 7, 2, 1]}) 

# Initial decay parameter. 
decay_constant = 1 

我們可以定義衰減權重爲exp(-time_delta * decay_constant),並設置其等於初始值之一:

df['weight'] = np.exp(-df.time.diff() * decay_constant) 
df.weight.iat[0] = 1 

>>> df 
    A time weight 
0 1 1.00 1.000000 
1 3 2.10 0.332871 
2 -1 2.13 0.970446 
3 7 3.70 0.208045 
4 2 10.00 0.001836 
5 1 10.20 0.818731 

現在,我們將採用JIT從numba優化發電機函數計算指數和:

from numba import jit 

@jit(nopython=True) 
def exponential_sum(A, k): 
    total = A[0] 
    yield total 
    for i in xrange(1, len(A)): # Use range in Python 3. 
     total = total * k[i] + A[i] 
     yield total 

,我們將使用產生的值添加到數據幀:

df['expSum'] = list(exponential_sum(df.A.values, df.weight.values)) 

產生所需的輸出:

>>> df 
    A time weight expSum 
0 1 1.00 1.000000 1.000000 
1 3 2.10 0.332871 3.332871 
2 -1 2.13 0.970446 2.234370 
3 7 3.70 0.208045 7.464850 
4 2 10.00 0.001836 2.013708 
5 1 10.20 0.818731 2.648684 

因此,讓我們的規模,以500萬行和檢查性能:使其均勻分佈的

df = pd.DataFrame({'time': np.random.rand(5e6).cumsum(), 'A': np.random.randint(1, 10, 5e6)}) 
df['weight'] = np.exp(-df.time.diff() * decay_constant) 
df.weight.iat[0] = 1 

%%timeit -n 10 
df['expSum'] = list(exponential_sum(df.A.values, df.weight.values)) 
10 loops, best of 3: 726 ms per loop 
+0

我正在使用Cython的類似解決方案,但一直希望有一個聰明的使用numpy/scipy,我失蹤了。看來共識是否定的。這個答案的變體似乎是你能做的最好的。 –

0

擴大對answer你聯繫,我想出了採用以下方法。

首先,請注意:

exponential_sum[i] = column_to_sum[i] + 
    np.exp(-const*(time[i]-time[i-1])) * column_to_sum[i-1] + 
    np.exp(-const*(time[i]-time[i-2])) * column_to_sum[i-2] + ... 

所以主要的變化,使在產生weightspace以匹配上述公式。我繼續如下:

time = pd.Series(np.random.rand(10)).cumsum() 
weightspace = np.empty((10,10)) 
for i in range(len(time)): 
    weightspace[i] = time - time[i] 
weightspace = np.exp(weightspace) 

不要擔心矩陣的左下三角形,它不會被使用。順便說一下,必須有一種方法來生成沒有循環的權重空間。

那麼你如何從weightspace挑中的權重滾動功能略有變化:

def rollingsum(array): 
    weights = weightspace[len(array)-1][:len(array)] 
    # Convolve the array and the weights to obtain the result 
    a = np.dot(array, weights).sum() 
    return a 

按預期工作:

dataset = pd.DataFrame(np.random.rand(10,3), columns=["A", "B","C"]) 
a = pd.expanding_apply(dataset, rollingsum) 
+0

對此解決方案的一個擔憂是權重空間現在非常大。在常規情況的解決方案中,數據幀的大小是線性的,現在它是二次的。這使得大型畫面出現問題。大幀是爲什麼需要矢量化解決方案。這是不可避免的? –

+0

針對@Alexander這樣的優化循環提示我擔心我看不到另一種方式。 – IanS