2016-08-01 56 views
4

應用函數對於大型數據框(大約1〜3百萬行)似乎工作得很慢。大熊貓 - 應用函數緩慢的說明

我已經在這裏檢查相關的問題,比如Speed up Pandas apply function,並且Counting within pandas apply() function,似乎加快它的最好的辦法是不使用應用功能:)

對於我來說,我有兩種工作要做與應用功能。

第一:與查找字典查詢申請

f(p_id, p_dict): 
    return p_dict[p_dict['ID'] == p_id]['value'] 

p_dict = DataFrame(...) # it's another dict works like lookup table 
df = df.apply(f, args=(p_dict,)) 

二:與GROUPBY

f(week_id, min_week_num, p_dict): 
    return p_dict[(week_id - min_week_num < p_dict['WEEK']) & (p_dict['WEEK'] < week_id)].ix[:,2].mean() 

f_partial = partial(f, min_week_num=min_week_num, p_dict=p_dict) 
df = map(f, df['WEEK']) 

我猜拳頭案件,不適用,它可以與數據幀進行聯接,而我不知道關於大數據集上的這種聯接的資源成本。

我的問題是:

  1. 有什麼辦法來替代適用於上述兩種情況?
  2. 爲什麼申請這麼慢?對於字典查找案例,我認爲它應該是O(N),即使N是100萬,也不應該花費太多。
+1

你試圖做的第一部分是什麼?從f的定義來看,我懷疑它沒有做你想做的事情。也許你應該添加數據框的小例子和預期的結果。 –

+0

感謝Ami,對於第一個問題,例如,p_dict是國家ID與國名查找表,我想查詢國名與國家ID作爲輸入,如果沒有國家ID存在,返回NA :) – linpingta

回答

2

關於你的第一個問題,我不能確切地說明爲什麼這個實例很慢。但通常,apply不利用矢量化。另外,apply會返回一個新的Series或DataFrame對象,所以對於一個非常大的DataFrame,您會有相當大的IO開銷(我無法保證自Pandas加載內部實現優化以來100%的時間就是這種情況)。

對於您的第一種方法,我假設您正在嘗試使用p_dict作爲查找表填充df中的「值」列。這是約1000倍更快地使用pd.merge

import string, sys 

import numpy as np 
import pandas as pd 

## 
# Part 1 - filling a column by a lookup table 
## 
def f1(col, p_dict): 
    return [p_dict[p_dict['ID'] == s]['value'].values[0] for s in col] 

# Testing 
n_size = 1000 
np.random.seed(997) 
p_dict = pd.DataFrame({'ID': [s for s in string.ascii_uppercase], 'value': np.random.randint(0,n_size, 26)}) 
df = pd.DataFrame({'p_id': [string.ascii_uppercase[i] for i in np.random.randint(0,26, n_size)]}) 

# Apply the f1 method as posted 
%timeit -n1 -r5 temp = df.apply(f1, args=(p_dict,)) 
>>> 1 loops, best of 5: 832 ms per loop 

# Using merge 
np.random.seed(997) 
df = pd.DataFrame({'p_id': [string.ascii_uppercase[i] for i in np.random.randint(0,26, n_size)]}) 
%timeit -n1 -r5 temp = pd.merge(df, p_dict, how='inner', left_on='p_id', right_on='ID', copy=False) 

>>> 1000 loops, best of 5: 826 µs per loop 

關於第二個任務,我們可以快速添加一個新列p_dict計算在時間窗開始於min_week_num並在一週該行中結尾的平均p_dict。這要求p_dict沿着WEEK列升序排列。然後你可以再次使用pd.merge

我假設在以下示例中min_week_num爲0。但是您可以輕鬆修改rolling_growing_mean以獲得不同的價值。由於它每次迭代進行固定次數的操作,因此該方法將在O(n)中運行。

n_size = 1000 
np.random.seed(997) 
p_dict = pd.DataFrame({'WEEK': range(52), 'value': np.random.randint(0, 1000, 52)}) 
df = pd.DataFrame({'WEEK': np.random.randint(0, 52, n_size)}) 

def rolling_growing_mean(values): 
    out = np.empty(len(values)) 
    out[0] = values[0] 
    # Time window for taking mean grows each step 
    for i, v in enumerate(values[1:]): 
     out[i+1] = np.true_divide(out[i]*(i+1) + v, i+2) 
    return out 

p_dict['Means'] = rolling_growing_mean(p_dict['value']) 

df_merged = pd.merge(df, p_dict, how='inner', left_on='WEEK', right_on='WEEK') 
+0

謝謝安德魯,真的對第一個問題很好,善良:)但我想你可能會誤解我的第二個問題,因爲week_id是輸入參數,例如week_id = 5,min_week_num = 2,那麼我想查詢p_dict ['WEEK']值的意思與3-4,而week_id = 6,查詢4-5,所以我想這不等於把它移出。 – linpingta

+0

@linpingta當然是。我在想數學,而不是編碼!我刪除了這部分答案。 – andrew

+0

那麼有什麼辦法解決這個問題?我知道也許我應該把它帶到另一個問題,但任何建議,我將不勝感激:) – linpingta