2016-08-20 42 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