コードの最適化を何度も試みた後、最後のリソースは、複数のコアを使用して以下のコードを実行しようとすることであるように思われます。複数のコアを使用してコードをはるかに高速に実行できるように、コードを変換/再構築する方法が正確にわかりません。最終目標を達成するためのガイダンスをいただければ幸いです。最終的な目標は、各配列が約700,000個の要素を保持する配列AおよびBに対して、このコードを可能な限り高速に実行できるようにすることです。小さな配列を使用したコードは次のとおりです。 700k要素の配列はコメント化されています。
_import numpy as np
def ismember(a,b):
for i in a:
index = np.where(b==i)[0]
if index.size == 0:
yield 0
else:
yield index
def f(A, gen_obj):
my_array = np.arange(len(A))
for i in my_array:
my_array[i] = gen_obj.next()
return my_array
#A = np.arange(700000)
#B = np.arange(700000)
A = np.array([3,4,4,3,6])
B = np.array([2,5,2,6,3])
gen_obj = ismember(A,B)
f(A, gen_obj)
print 'done'
# if we print f(A, gen_obj) the output will be: [4 0 0 4 3]
# notice that the output array needs to be kept the same size as array A.
_
私がやろうとしているのは、 ismember [2]というMATLAB関数を模倣することです(次のようにフォーマットされています:[Lia,Locb] = ismember(A,B)
。Locb
部分のみ。
Matlabから:Locb、BのメンバーであるAの各値のBの最小インデックスを含みます。出力配列Locbは、AがBのメンバーでない場合は常に0を含みます。
主な問題の1つは、この操作を可能な限り効率的に実行できる必要があることです。テスト用に、700k要素の2つの配列があります。ジェネレーターを作成してジェネレーターの値を調べることは、仕事を早く終わらせるようには見えません。
複数のコアについて心配する前に、辞書を使用してismember関数の線形スキャンを排除します。
_def ismember(a, b):
bind = {}
for i, elt in enumerate(b):
if elt not in bind:
bind[elt] = i
return [bind.get(itm, None) for itm in a] # None can be replaced by any other "not in b" value
_
元の実装では、Aの要素ごとにBの要素を完全にスキャンして、O(len(A)*len(B))
にする必要があります。上記のコードでは、dictBsetを生成するためにBを1回フルスキャンする必要があります。 dictを使用すると、Aの各要素に対してBの各要素のルックアップを効果的に一定にし、操作をO(len(A)+len(B))
にします。それでも遅すぎる場合は、上記の関数を複数のコアで実行することを心配してください。
編集:インデックスも少し変更しました。 Matlabは、すべての配列がインデックス1で始まるため、0を使用します。Python/ numpyは配列を0で開始するため、データセットの場合は次のようになります。
_A = [2378, 2378, 2378, 2378]
B = [2378, 2379]
_
要素がない場合は0を返すと、結果はAのすべての要素を除外します。上記のルーチンは、インデックスがない場合は0ではなくNone
を返します。-1を返すことはオプションですが、Pythonは、それが配列の最後の要素であると解釈します。None
は、配列へのインデックスとして使用される場合、例外を発生させます。別の動作が必要な場合は、Bind.get(item,None)
の2番目の引数を変更してください。返される値への式。
sfstewmanの優れた回答は、おそらくあなたのために問題を解決しました。
Numpyだけで同じことを達成する方法を追加したいと思います。
B_unique_sorted, B_idx = np.unique(B, return_index=True)
B_in_A_bool = np.in1d(B_unique_sorted, A, assume_unique=True)
B_unique_sorted
には、ソートされたB
内の一意の値が含まれます。B_idx
は、これらの値に対して、元のB
へのインデックスを保持します。B_in_A_bool
は、B_unique_sorted
の値がA
にあるかどうかを格納するB_unique_sorted
のサイズのブール配列です。B_idx
に関して返されますA
はすでに一意であると思います。これで、B_in_A_bool
を使用して共通の値を取得できます
B_unique_sorted[B_in_A_bool]
および元のB
のそれぞれのインデックス
B_idx[B_in_A_bool]
最後に、テストはしていませんが、これは純粋なPython forループよりも大幅に高速であると思います。
リスト内包表記を使用してみてください。
_In [1]: import numpy as np
In [2]: A = np.array([3,4,4,3,6])
In [3]: B = np.array([2,5,2,6,3])
In [4]: [x for x in A if not x in B]
Out[4]: [4, 4]
_
一般に、リスト内包表記はforループよりもはるかに高速です。
等しい長さのリストを取得するには;
_In [19]: map(lambda x: x if x not in B else False, A)
Out[19]: [False, 4, 4, False, False]
_
これは、小さなデータセットの場合は非常に高速です。
_In [20]: C = np.arange(10000)
In [21]: D = np.arange(15000, 25000)
In [22]: %timeit map(lambda x: x if x not in D else False, C)
1 loops, best of 3: 756 ms per loop
_
大規模なデータセットの場合は、multiprocessing.Pool.map()
を使用して操作を高速化してみてください。
これは、MATLABに一致する両方の出力引数[Lia、Locb]を返す正確なMATLABの同等物です。ただし、Python 0も有効なインデックスです。したがって、この関数は0を返しません。基本的にLocb(Locb> 0)を返します。パフォーマンスもMATLABと同等です。
def ismember(a_vec, b_vec):
""" MATLAB equivalent ismember function """
bool_ind = np.isin(a_vec,b_vec)
common = a[bool_ind]
common_unique, common_inv = np.unique(common, return_inverse=True) # common = common_unique[common_inv]
b_unique, b_ind = np.unique(b_vec, return_index=True) # b_unique = b_vec[b_ind]
common_ind = b_ind[np.isin(b_unique, common_unique, assume_unique=True)]
return bool_ind, common_ind[common_inv]
少し(〜5x)遅いが、固有の関数を使用しない代替実装は次のとおりです。
def ismember(a_vec, b_vec):
''' MATLAB equivalent ismember function. Slower than above implementation'''
b_dict = {b_vec[i]: i for i in range(0, len(b_vec))}
indices = [b_dict.get(x) for x in a_vec if b_dict.get(x) is not None]
booleans = np.in1d(a_vec, b_vec)
return booleans, np.array(indices, dtype=int)