numpy.where(condition)的输出不是一个数组,而是一个数组元组:为什么?

53

我正在尝试使用 numpy.where(condition[, x, y]) 函数。
根据numpy文档,如果你只给一个数组作为输入,它应该返回数组中非零元素(即“True”)的索引:

如果只有 condition 被给出,返回元组 condition.nonzero(),其中包含 condition 为真的索引。

但是如果我尝试它,它会返回一个包含两个元素的 元组,第一个元素是想要的索引列表,第二个元素是空元素:

>>> import numpy as np
>>> array = np.array([1,2,3,4,5,6,7,8,9])
>>> np.where(array>4)
(array([4, 5, 6, 7, 8]),) # notice the comma before the last parenthesis

所以问题是:为什么?这种行为的目的是什么?在什么情况下会有用呢? 实际上,为了得到想要的索引列表,我必须添加索引,如np.where(array>4)[0],看起来...“丑陋”。


补充说明

我理解(根据某些答案)它实际上是只有一个元素的元组。但我仍然不明白为什么要以这种方式输出。为了说明这不是理想的方法,请考虑以下错误(这也是我提出问题的原因):

>>> import numpy as np
>>> array = np.array([1,2,3,4,5,6,7,8,9])
>>> pippo = np.where(array>4)
>>> pippo + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate tuple (not "int") to tuple

所以你需要进行一些索引才能访问实际的索引数组:

>>> pippo[0] + 1
array([5, 6, 7, 8, 9])

4
np.argwhere 怎么样? - Divakar
6
СйажюђУдЂуџёТў»np.flatnonzero№╝їт«ЃТЅДУАїуџёТЊЇСйюТў»a.ravel().nonzero()[0]сђѓ - hpaulj
3个回答

49
在Python中,(1)表示只有一个元素的集合 1。为了方便人类阅读,在数字和表达式中可以自由添加括号(例如:(1+3)*3(1+3,)*3)。因此,如果要表示只有一个元素的元组,需要使用(1,)(同时也需要将其用作这个元组)。
(array([4, 5, 6, 7, 8]),)

([array],)是一个只有一个元素的元组,该元素是一个数组。

如果你将where应用于一个二维数组,结果将是一个有两个元素的元组。

where的结果可以直接插入到索引插槽中,例如:

a[where(a>0)]
a[a>0]

应该返回相同的内容

就像会返回的一样

I,J = where(a>0)   # a is 2d
a[I,J]
a[(I,J)]

或者以您的示例为例:
In [278]: a=np.array([1,2,3,4,5,6,7,8,9])
In [279]: np.where(a>4)
Out[279]: (array([4, 5, 6, 7, 8], dtype=int32),)  # tuple

In [280]: a[np.where(a>4)]
Out[280]: array([5, 6, 7, 8, 9])

In [281]: I=np.where(a>4)
In [282]: I
Out[282]: (array([4, 5, 6, 7, 8], dtype=int32),)
In [283]: a[I]
Out[283]: array([5, 6, 7, 8, 9])

In [286]: i, = np.where(a>4)   # note the , on LHS
In [287]: i
Out[287]: array([4, 5, 6, 7, 8], dtype=int32)  # not tuple
In [288]: a[i]
Out[288]: array([5, 6, 7, 8, 9])
In [289]: a[(i,)]
Out[289]: array([5, 6, 7, 8, 9])

======================

np.flatnonzero 展示了正确的方式,无论输入数组的维度如何,都可以返回一个数组。

In [299]: np.flatnonzero(a>4)
Out[299]: array([4, 5, 6, 7, 8], dtype=int32)
In [300]: np.flatnonzero(a>4)+10
Out[300]: array([14, 15, 16, 17, 18], dtype=int32)

它的文档说:

这等同于 a.ravel().nonzero()[0]

实际上,这确实是该函数的作用。

通过将 a 扁平化,消除了处理多个维度的问题。然后它从元组中取出响应,给你一个普通的数组。通过扁平化,它不必为 1d 数组制定特殊情况。

===========================

@Divakar 建议使用 np.argwhere

In [303]: np.argwhere(a>4)
Out[303]: 
array([[4],
       [5],
       [6],
       [7],
       [8]], dtype=int32)

这段代码的作用是将数组a中大于4的元素的索引转置并输出,代码为np.transpose(np.where(a>4))

如果您不喜欢列向量,也可以再次进行转置。

In [307]: np.argwhere(a>4).T
Out[307]: array([[4, 5, 6, 7, 8]], dtype=int32)

除了现在它是一个1xn数组。

我们可以同样地将where封装在array中:

In [311]: np.array(np.where(a>4))
Out[311]: array([[4, 5, 6, 7, 8]], dtype=int32)

有许多方法可以从 where 元组中取出数组([0]i,=transposearray等)。


1
嗨@hpaulj,感谢您的回答。我明白了,这是一个只有1个元素的元组。但是,您仍然需要索引才能访问实际数组,而我不明白为什么,即为什么以那种方式给出输出...请参见编辑后问题中的补充说明。 - Fabio
1
目标是确保所有数组的一致性。np.flatnonzero 显示了正确的方式,返回一个数组而不是元组。 - hpaulj
那么,为什么在文档中会写“返回:out: ndarray 一个带有元素的数组…”这句话呢?这就是让我感到困惑的地方。 - Bill
@Bill,那个out是用于三个参数的情况。在SO上,我们经常只使用条件参数。现在的文档建议使用np.nonzero来实现这个目的。 - hpaulj
啊,是的,谢谢@hpaulj。现在我看到页面顶部附近的注释了,它说:“其余文档仅涵盖提供了三个参数的情况。” - Bill

12

简短回答:无论数组的维数如何,np.where 都旨在具有一致的输出。

一个二维数组有两个索引,因此 np.where 的结果是一个长度为2的元组,其中包含相关的索引。对于三维数组,结果将变为长度为3的元组,四维则为长度为4的元组,或者长度为N的元组(如果是N维)。按照这个规则,在1维中,结果应该是一个长度为1的元组。


我不明白为什么不使用n维数组,其中第n个轴对应第n个维度?元组的不同元素长度可以不同吗? - Gulzar
使用数组进行索引与使用元组进行索引具有不同的语义。如果结果是ndarray而不是元组,则x[np.where(x == 0)]将不会返回数组中的零元素。 - jakevdp

1

只需使用np.asarray函数。在您的情况下:

>>> import numpy as np
>>> array = np.array([1,2,3,4,5,6,7,8,9])
>>> pippo = np.asarray(np.where(array>4))
>>> pippo + 1
array([[5, 6, 7, 8, 9]])

为什么要使用np.asarray()而不是np.array() - user7345804

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接