2016-05-02 10 views
4

私は2次元numpy配列(すべて同じ形状)のPythonリストを持っており、等価配列のインデックスを抽出したいと思います。私はこの思い付いた:等価numpy 2D行のインデックスを検索

a = np.array([[1, 2], [3, 4]]) 
b = np.array([[1, 2], [3, 4]]) 
c = np.array([[3, 4], [1, 2]]) 
d = np.array([[3, 4], [1, 2]]) 
e = np.array([[3, 4], [1, 2]]) 
f = np.array([[1, 2], [3, 4]]) 
g = np.array([[9, 9], [3, 4]]) 

li = [a, b, c, d, e, f, g] 

indexes = list(range(len(li))) 
equals = [] 
for i, a_i in enumerate(indexes): 
    a_equals = [] 
    for j, b_i in enumerate(indexes[i+1:]): 
     if np.array_equal(li[a_i], li[b_i]): 
      del indexes[j] 
      a_equals.append(b_i) 
    if a_equals: 
     equals.append((a_i, *a_equals)) 

print(equals) 
# [(0, 1, 5), (2, 3, 4)] 

それは動作します(あなたは2次元配列のどれもが空でないと仮定することができます)しかしソリューションは不格好、おそらく遅いです。 Numpyでこれをもっとエレガントにする方法はありますか?

+0

は、同一の形状のすべてのものを2次元配列で前処理ステップとしてnpi.multiplicity> 1を使用することができますが、シングルカウントインデックスを削除する確か

は、おそらく最高の、後処理ステップとして残っていますか? – Divakar

+0

はい。常に同じ形。 – tsorn

+0

出力の行の順序が重要です。つまり、代わりに '[(2,3,4)(0、1、5)]'を取得するとどうなりますか? – Divakar

答えて

1

リストに入力配列が同一形状であるという事実を考慮しようとすることができ、各要素を表す各列で、単一の2Dアレイに配列のリストを連結することができ入力リストのこれにより、さらなる計算が容易になり、ベクトル化された演算が容易になる。実装は、このようになります -

# Concatenate all elements into a 2D array 
all_arr = np.concatenate(li).reshape(-1,li[0].size) 

# Reduce each row with IDs such that each they represent indexing tuple 
ids = np.ravel_multi_index(all_arr.T,all_arr.max(0)+1) 

# Tag each such IDs based on uniqueness against other IDs 
_,unqids,C = np.unique(ids,return_inverse=True,return_counts=True) 

# Sort the unique IDs and split into groups for final output 
sidx = unqids.argsort() 

# Mask corresponding to unqids that has ID counts > 1 
mask = np.in1d(unqids,np.where(C>1)[0]) 

# Split masked sorted indices at places corresponding to cumsum-ed counts 
out = np.split(sidx[mask[sidx]],C[C>1].cumsum())[:-1] 

注:連結入力配列all_arrの列の膨大な数がある場合は、あなたがそうのように、idsを手動np.cumprodを使用してインデックスを取得したい場合があります -

ids = all_arr.dot(np.append(1,(all_arr.max(0)+1)[::-1][:-1].cumprod())[::-1]) 
+0

これは確かにオールナンピーソリューションであり、非常に高速です(私のソリューションで0.002秒と2秒、大きな入力で@gdlmxソリューションで2秒)が、周波数1の配列のインデックスが含まれています。 'out'から長さ1のすべての配列が、それを行う良い方法はありますか? – tsorn

+0

@tsornもう少し作業が必要でした。その要件をカバーする更新されたバージョンを追加しました。チェックアウトしてください。 – Divakar

+0

ravel_multi_indexを使ってユニークなサブアレイをエンコードするのは賢明です。配列が高次元になるか、最大値が大きくなるとオーバーフロー問題に遭遇するかもしれないと私は考えています。 –

0

たぶんitertools

import itertools 
from collections import defaultdict 

equals=defaultdict(list) 
visited=[] 
for a, b in itertools.combinations(enumerate(li), 2): 
    if not b[0] in visited and np.array_equal(a[1], b[1]) : 
    equals[a[0]].append(b[0]) 
    visited += (a[0],b[0]) 

print equals 
# defaultdict(<type 'list'>, {0: [1, 5], 2: [3, 4]}) 
0

この問題は、エレガントnumpy_indexedパッケージを使用して解決することができる(免責事項:私はその作者午前):

これらのインデックスを見つけることは最終目標ではないと思われます。numpy_indexedを少し使ってみると、最終目標へのより直接的なルートが存在することがあります。あなたも

関連する問題