2016-07-08 11 views
3

私は2つのパンダデータフレームAとBを持っています。2つのパンダのデータフレームの行を比較する最速の方法は?

Aは存在または不在を示すバイナリ値で埋められています。

Bは、1024行×10列であり、0と1の完全な反復であり、したがって1024行を有する。

Aの特定の10桁のAのどの行がBの特定の行に対応しているかを調べようとしています。要素ごとではなく、全体の行が一致する必要があります。

例えば、私は

A[(A.ix[:,(1,2,3,4,5,6,7,8,9,10)==(1,0,1,0,1,0,0,1,0,0)).all(axis=1)] 

は、それらの特定の列(1,2,3,4,5,6,7,8,9,10)

でBのそれ(1,0,1,0,1,0,0,1,0,0)行とマッチアップしている行(3,5,8,11,15)何かを返したいだろうと私はすべての上でこれをやりたいですB.内の行 私はこれを行うために見つけ出すことができる最高の方法は:

import numpy as np 
for i in B: 
    B_array = np.array(i) 
    Matching_Rows = A[(A.ix[:,(1,2,3,4,5,6,7,8,9,10)] == B_array).all(axis=1)] 
    Matching_Rows_Index = Matching_Rows.index 

これを1つのインスタンスではひどいわけではありませんが、私はそれを約2万回実行するwhileループで使用します。したがって、かなり減速します。

私はDataFrame.applyを使いこなしていません。仕事をよりよくマッピングできるか?

私はPythonにはかなり新しいですが、誰かが明らかに効率的であると思っていただけです。

ありがとうございます。

+1

Aの1つの行にBの一致が複数存在する可能性があります。したがって、Aの特定の行に少なくとも1つの一致が欲しいですか?それとも、col1-col10間のマッチを、Aの投稿コードだけで判断して探していますか? – Divakar

+1

一致する行はどうしますか?私はあなたがBを完全に避けて、興味のある10列に 'groupby'を使うことができるようです。 –

+0

私は、一致する行に基づいてエントロピー値を計算しようとしています。 –

答えて

2

あなたはreset_indexmergeを使用することができます - 出力をカスタム列にAに等しいBのインデックスです:

A = pd.DataFrame({'A':[1,0,1,1], 
        'B':[0,0,1,1], 
        'C':[1,0,1,1], 
        'D':[1,1,1,0], 
        'E':[1,1,0,1]}) 

print (A) 
    A B C D E 
0 1 0 1 1 1 
1 0 0 0 1 1 
2 1 1 1 1 0 
3 1 1 1 0 1 

B = pd.DataFrame({'0':[1,0,1], 
        '1':[1,0,1], 
        '2':[1,0,0]}) 

print (B) 
    0 1 2 
0 1 1 1 
1 0 0 0 
2 1 1 0 
print (pd.merge(B.reset_index(), 
       A.reset_index(), 
       left_on=B.columns.tolist(), 
       right_on=A.columns[[0,1,2]].tolist(), 
       suffixes=('_B','_A'))) 

    index_B 0 1 2 index_A A B C D E 
0  0 1 1 1  2 1 1 1 1 0 
1  0 1 1 1  3 1 1 1 0 1 
2  1 0 0 0  1 0 0 0 1 1  

print (pd.merge(B.reset_index(), 
       A.reset_index(), 
       left_on=B.columns.tolist(), 
       right_on=A.columns[[0,1,2]].tolist(), 
       suffixes=('_B','_A'))[['index_B','index_A']])  

    index_B index_A 
0  0  2 
1  0  3 
2  1  1 
+0

ありがとうございます!これは、私のコードの他の変更と共に、実行時間を10時間から10分に短縮しました。 –

+0

@ jezrael。私は問題を正しく理解していないかもしれませんが、上記の解決策では、ソリューションの中で指定された範囲外のAの行をどのように比較しますか?Aの列22:32はBの行7に対応します。私の理解によれば、あなたは比較目的(すなわちcols 0,1,2)のためのA列の参照リストをハードコアしています –

1

pandasでは、locまたはixを使って、10列がすべて等しい行を見つけるように指示することができます。このように:

A.loc[(A[1]==B[1]) & (A[2]==B[2]) & (A[3]==B[3]) & A[4]==B[4]) & (A[5]==B[5]) & (A[6]==B[6]) & (A[7]==B[7]) & (A[8]==B[8]) & (A[9]==B[9]) & (A[10]==B[10])] 

これは私の意見では非常に醜いですが、それは動作しますし、それが大幅に高速化する必要がありますので、ループを取り除きます。誰かが同じ操作をよりうまくコーディングするより洗練された方法を考え出すことができたら、私は驚くことはありません。

3

我々は両方のデータフレームは、バイナリ値0かを持っているという事実を悪用することができます1から、対応する列をAから畳み込み、すべての列をBから1Dの配列にそれぞれ折りたたむことで、各行を10進数に相当する2進数。これにより、問題のセットが大幅に削減され、パフォーマンスに役立ちます。ここで1Dの配列を取得した後、を使用してBの一致をAに、最後にnp.whereを検索して一致するインデックスを取得できます。

# Setup 1D arrays corresponding to selected cols from A and entire B 
S = 2**np.arange(10) 
A_ID = np.dot(A[range(1,11)],S) 
B_ID = np.dot(B,S) 

# Look for matches that exist from B_ID in A_ID, whose indices 
# would be desired row indices that have matched from B 
out_row_idx = np.where(np.in1d(A_ID,B_ID))[0] 

サンプル実行 - -

したがって、我々はそうのように実装しています

In [157]: # Setup dataframes A and B with rows 0, 4 in A having matches from B 
    ...: A_arr = np.random.randint(0,2,(10,14)) 
    ...: B_arr = np.random.randint(0,2,(7,10)) 
    ...: 
    ...: B_arr[2] = A_arr[4,1:11] 
    ...: B_arr[4] = A_arr[4,1:11] 
    ...: B_arr[5] = A_arr[0,1:11] 
    ...: 
    ...: A = pd.DataFrame(A_arr) 
    ...: B = pd.DataFrame(B_arr) 
    ...: 

In [158]: S = 2**np.arange(10) 
    ...: A_ID = np.dot(A[range(1,11)],S) 
    ...: B_ID = np.dot(B,S) 
    ...: out_row_idx = np.where(np.in1d(A_ID,B_ID))[0] 
    ...: 

In [159]: out_row_idx 
Out[159]: array([0, 4]) 
+0

私はこれに関するフォローアップの質問を持っています、私はこのメソッドの効率が本当に好きですが、私のデータフレームを使って実行すると、 'out_row_idx'はAのすべてのインデックスを含むサイズ1000の配列を返します。それはなぜでしょうか? –

+0

'S = 2 ** np.arange(10); A_ID = np.dot(A [X]、S); B_ID = np.dot(B、S); out_row_idx = np.where(np.in1d(A_ID、B_ID))[0] ' –

0

この特別な場合を、10個の0と1のあなたの行は、10桁のバイナリとして解釈することができます。 Bが整っていれば、0から1023までの範囲として解釈できます。この場合、Aの行を10列のチャンクに入れ、2進数の同等物を計算するだけです。

私は2のべき乗の範囲を定義することから始めます。そのため、行列乗算を行うことができます。

twos = pd.Series(np.power(2, np.arange(10))) 

次に、私は10。最後

A = pd.DataFrame(np.random.binomial(1, .5, (1000, 500))) 
A.columns = pd.MultiIndex.from_tuples(zip((A.columns/10).tolist(), (A.columns % 10).tolist())) 
A_ = A.stack(0) 

A_.head() 

enter image description here

の私のチャンクを取得するためにマルチインデックスとstackにAの列を再ラベル付けましょう、私が取得するtwosA_を掛けます各行の整数表現とunstackです。

A_.dot(twos).unstack() 

enter image description here

これは今、各セルがあっても必要がありません。私たちはAのその特定の行について、その特定の10カラムチャンクのマッチBの行のどの表し1000×50データフレームでありますBのために

関連する問題