9

各バンド内にいくつかの数値で構成される 3D 配列があります。配列が複数の条件を満たすインデックス位置を返す関数はありますか?

私は次のことを試しました:

index_pos = numpy.where(
    array[:,:,0]==10 and array[:,:,1]==15 and array[:,:,2]==30)

次のエラーが返されます。

ValueError: The truth value of an array with more than one element is ambiguous.
Use a.any() or a.all()
4

3 に答える 3

12

実際には、次のことを行う方が簡単で効率的な特別なケースがあります。

データを作成します。

>>> arr
array([[[ 6,  9,  4],
        [ 5,  2,  1],
        [10, 15, 30]],

       [[ 9,  0,  1],
        [ 4,  6,  4],
        [ 8,  3,  9]],

       [[ 6,  7,  4],
        [ 0,  1,  6],
        [ 4,  0,  1]]])

期待値:

>>> index_pos = np.where((arr[:,:,0]==10) & (arr[:,:,1]==15) & (arr[:,:,2]==30))
>>> index_pos
(array([0]), array([2]))

ブロードキャストを使用して、これを同時に行います。

>>> arr == np.array([10,15,30])
array([[[False, False, False],
        [False, False, False],
        [ True,  True,  True]],

       [[False, False, False],
        [False, False, False],
        [False, False, False]],

       [[False, False, False],
        [False, False, False],
        [False, False, False]]], dtype=bool)

>>> np.where( np.all(arr == np.array([10,15,30]), axis=-1) )
(array([0]), array([2]))

必要なインデックスが連続していない場合は、次のようにすることができます。

ind_vals = np.array([0,2])
where_mask = (arr[:,:,ind_vals] == values)

できたら放送してください。

@Jamie のコメントに刺激されて、考慮すべき興味深い点がいくつかあります。

arr = np.random.randint(0,100,(5000,5000,3))

%timeit np.all(arr == np.array([10,15,30]), axis=-1)
1 loops, best of 3: 614 ms per loop

%timeit ((arr[:,:,0]==10) & (arr[:,:,1]==15) & (arr[:,:,2]==30))
1 loops, best of 3: 217 ms per loop

%timeit tmp = (arr == np.array([10,15,30])); (tmp[:,:,0] & tmp[:,:,1] & tmp[:,:,2])
1 loops, best of 3: 368 ms per loop

質問は、なぜこれなのか?:

最初に調べる:

%timeit (arr[:,:,0]==10)
10 loops, best of 3: 51.2 ms per loop

%timeit (arr == np.array([10,15,30]))
1 loops, best of 3: 300 ms per loop

arr == np.array([10,15,30])最悪の場合は の 1/3 の速度になると予想されarr[:,:,0]==10ます。なぜこれが当てはまらないのか、誰にも分かりますか?

次に、最終的な軸を組み合わせるときに、これを達成する方法はたくさんあります。

tmp = (arr == np.array([10,15,30]))

method1 = np.all(tmp,axis=-1)
method2 = (tmp[:,:,0] & tmp[:,:,1] & tmp[:,:,2])
method3 = np.einsum('ij,ij,ij->ij',tmp[:,:,0] , tmp[:,:,1] , tmp[:,:,2])

np.allclose(method1,method2)
True
np.allclose(method1,method3)
True

%timeit np.all(tmp,axis=-1)
1 loops, best of 3: 318 ms per loop

%timeit (tmp[:,:,0] & tmp[:,:,1] & tmp[:,:,2])
10 loops, best of 3: 68.2 ms per loop

%timeit np.einsum('ij,ij,ij->ij',tmp[:,:,0] , tmp[:,:,1] , tmp[:,:,2])
10 loops, best of 3: 38 ms per loop

einsum の高速化は別の場所allで明確に定義されていますが、と連続した の間にこのような違いがあるのは奇妙に思え&ます。

于 2013-11-04T15:32:16.803 に答える
4

問題は、ネイティブの Pythonandキーワードの使用です。これは、配列に対して希望どおりに動作しません。

代わりに、numpy.logical_and関数を使用してみてください。

cond1 = np.logical_and(array[:,:,0]==10, array[:,:,1]==15)
cond2 = np.logical_and(cond1, array[:,:,2]==30)
index_pos = numpy.where(cond2)

logical_and任意の数の条件を受け入れる独自のバージョンを作成することもできます。

def my_logical_and(*args):
    return reduce(np.logical_and, args)

condition_locs_and_vals = [(0, 10), (1, 15), (2, 30)]
conditions = [array[:,:,x] == y for x,y in conditition_locs_and_vals]
my_logical_and(*conditions)

bitwise-and ( &) の使用は機能しますが、偶然にすぎません。ビット単位の and は、ビットまたはbool型を比較す​​るためのものです。これを使用して数値配列の真の値を比較するのは堅牢ではありません (たとえば、実際に最初に配列Trueに変換するのではなく、エントリが評価される場所にインデックスを付ける必要がある場合など)。代わりに実際に使用する必要があります(速度のペナルティが伴う場合でも)。boollogical_and&

また、条件の任意のリストを で連鎖さ&せると、読むのも入力するのも苦痛になる可能性があります。また、コードの再利用性のために、後のプログラマーが一連の従属節を&演算子に変更する必要がないように、個々の条件を個別に保存してから、上記のような関数を使用することをお勧めします。それらを組み合わせる。

于 2013-11-04T14:57:30.187 に答える