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])在更坏的情况下,速度将是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得到了很好的定义,但对我来说,和Continuous之间存在这样的差异似乎很奇怪&

于 2013-11-04T15:32:16.803 回答
6

The and operator won't work in this case.

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

Give this a try:

index_pos = numpy.where((array[:,:,0]==10) & (array[:,:,1]==15) & (array[:,:,2]==30))
于 2013-11-04T14:58:38.017 回答
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)

使用按位与 ( &) 有效,但只是巧合。按位与用于比较位或bool类型。使用它来比较数值数组的真值并不可靠(例如,如果您突然需要对条目求值的位置进行索引,True而不是实际首先转换为bool数组)。logical_and真的应该使用而不是&(即使它带有速度惩罚)。

此外,将任意条件列表链接在一起&可能会让人难以阅读和输入。并且为了代码的可重用性,让后面的程序员不必为&运算符更改一堆从属子句,最好将各个条件单独存储,然后使用类似上面的函数将它们结合起来。

于 2013-11-04T14:57:30.187 回答