2016-08-20 5 views
3

反復GROUPBY操作のパフォーマンスの向上:このデータフレームからは、基本的にはバイナリ行列であり、Iはマルチインデックスを有するデータフレームを有する

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の数...(値の数)を取得:

0 2 
1 5 
2 2 
3 1 

私はこのFを行う必要がありますがまたはセッションも可能です。各行のセッション合計:

  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) 

、次の2つの方法がそれぞれ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を修正する必要がありました。

私は現在、直接グループを再計算せずにカウントするデータフレームの変更を反映するために、別の方法に取り組んでいますが、私はまだ成功していません。影響を受けた行を追跡し、保存されたDataFramesを更新するのが遅いことが判明しました。

これらのgroupby操作のパフォーマンスを向上させる方法はありますか?あなたが唯一興味を持っている場合は

# 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) 

- 定期的なデータ形式(日、各行間のセッションの数と同じ数)を想定すると

+0

トンにelemsの順番をい問題の出力は?さらに、2つの出力のインデックスは重要ですか? – Divakar

+0

いいえ、そこに0、1などがいくつあるのか知っていれば、注文(またはその情報を保持するデータ構造)は重要ではありません。私は0に対応するものを知るべきであり、1は1に対応する。 – ayhan

答えて

2

、ここでの出力はソート順にそのインデックスを持つnp.uniqueを使用してnumpyのベースのアプローチですnp.uniquereturn_counts一環で行われるよう、インデックスのない値で、ここでは基本的にただカウントを行いnp.bincountとの代替は、だ -

# 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

関連する問題