2016-08-20 75 views
3

我有一個數據幀以多指標的表現基本上是一個二元矩陣:提高重複GROUPBY操作

day  day01      day02     
session session1 session2 session3 session1 session2 session3 
0    1  0  0  0  0  0 
1    0  0  1  1  1  0 
2    1  1  1  0  0  1 
3    1  0  0  1  0  0 
4    1  0  1  0  0  0 

從這個數據幀,我需要計算每日的資金用於每行:

 day01 day02 
0  1  0 
1  1  2 
2  3  1 
3  1  1 
4  2  0 

並得到0,1秒的(值計數)這筆金額的數目...:我需要做此F

0 2 
1 5 
2 2 
3 1 

或會話。每一行會議和:

  session1 session2 session3 
0    1   0   0 
1    1   1   1 
2    1   1   2 
3    2   0   0 
4    1   0   1 

,並獲得價值數:

0 5 
1 8 
2 2 

爲基準,這是df.groupby(level='day', axis=1).sum().stack().value_counts()(和df.groupby(level='session', axis=1).sum().stack().value_counts())的結果。 DataFrame在模擬退火算法的每次迭代中都會發生變化,並重新計算這些計數。當我剖析代碼時,我發現在groupby操作上花費了大量的時間。

我試着保存groupby對象,並在每次迭代中對這些對象進行總計,但改進大約爲10%。下面的代碼創建一個更大的數據幀(類似於一個我):

import numpy as np 
import pandas as pd 
prng = np.random.RandomState(0) 
days = ['day{0:02d}'.format(i) for i in range(1, 11)] 
sessions = ['session{}'.format(i) for i in range(1, 5)] 
idx = pd.MultiIndex.from_product((days, sessions), names=['day', 'session']) 
df = pd.DataFrame(prng.binomial(1, 0.25, (1250, 40)), columns=idx) 

在我的電腦,以下兩種方法分別以3.8S和3.38s。

def try1(df, num_repeats=1000): 
    for i in range(num_repeats): 
     session_counts = (df.groupby(level='session', axis=1, sort=False) 
          .sum() 
          .stack() 
          .value_counts(sort=False)) 
     daily_counts = (df.groupby(level='day', axis=1, sort=False) 
          .sum() 
          .stack() 
          .value_counts(sort=False)) 
    return session_counts, daily_counts 

def try2(df, num_repeats=1000): 
    session_groups = df.groupby(level='session', axis=1, sort=False) 
    day_groups = df.groupby(level='day', axis=1, sort=False) 
    for i in range(num_repeats): 
     df.iat[0, 0] = (i + 1) % 2 
     session_counts = session_groups.sum().stack().value_counts(sort=False) 
     daily_counts = day_groups.sum().stack().value_counts(sort=False) 
    return session_counts, daily_counts 

%time try1(df) 
Wall time: 3.8 s 

%time try2(df) 
Wall time: 3.38 s 

注意:函數中的循環僅用於計時。對於第二個函數,爲了獲得正確的時序,我需要修改DataFrame。

我目前正在研究另一種方法,直接反映DataFrame中的更改計數沒有重新計算組,但我還沒有成功。跟蹤受影響的行並更新保存的數據幀結果變慢。

有沒有辦法提高這些groupby操作的性能?

+0

是否在T elems的順序窩產品很重要?另外,兩個產出的指標是否重要? – Divakar

+0

不,只要我知道有多少個0,1等,那麼順序(或哪個數據結構就包含這些信息)並不重要。我應該知道哪一個對應於0,哪一個對應於1。 – ayhan

回答

2

假設一個普通的數據格式(相當於天數,並且使各行的會話),這是一個使用np.unique以他們的指標的排序順序輸出NumPy的基礎的方法 -

# Extract array 
a,b = df.columns.levels 
arr = df.values.reshape(-1,len(a),len(b)) 

# Get session counts 
session_sums = arr.sum(1) 
unq,count = np.unique(session_sums,return_counts=True) 
session_counts_out = pd.Series(count,index=unq) 

# Get daily count 
daily_sums = arr.sum(2) 
unq,count = np.unique(daily_sums,return_counts=True) 
daily_counts_out = pd.Series(count,index=unq) 

如果你只關心在沒有指標的值,這裏是與np.bincount替代,基本上只是做計數,由return_counts部分與np.unique做 -

# Get session counts 
session_sums = arr.sum(1) 
count = np.bincount(session_sums.ravel()) 
session_counts_out = count[count>0] 

# Get daily count 
daily_sums = arr.sum(2) 
count = np.bincount(daily_sums.ravel()) 
daily_counts_out = count[count>0] 
+0

謝謝。看起來很有希望。讓我試試看。 – ayhan

+0

bincount比groupby快7倍左右(我刪除了'count [count> 0]'的部分,所以我可以通過索引訪問)。讓我繼續打開幾天,看看是否有其他的選擇。再次感謝你。 – ayhan

+0

@ayhan聽起來對我很好! – Divakar