Вывод numpy.where(condition) не является массивом, а кортежем массивов: почему?

Я экспериментирую с функцией numpy.where(condition[, x, y]).
Из документа numpy, я узнаю, что если вы укажете только один массив в качестве входных данных, он должен вернуть индексы, в которых массив не равен нулю (т.е. "True" ):

Если задано только условие, верните условие кортежа. nonzero(), индексы, где условие истинно.

Но если попробовать, он возвращает мне кортеж из двух элементов, где первый - это нужный список индексов, а второй - нулевой элемент:

>>> 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])

Ответы

Ответ 1

В Python (1) означает просто 1. () можно свободно добавлять к номерам групп и выражениям для удобочитаемости человека (например, (1+3)*3 v (1+3,)*3). Таким образом, для обозначения 1-элементного кортежа он использует (1,) (и требует, чтобы вы также использовали его).

Таким образом,

(array([4, 5, 6, 7, 8]),)

- это один элемент, который является массивом.

Если вы применили where к 2d-массиву, результатом будет 2-элементный кортеж.

Результат 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 устраняет вопрос о том, что делать с несколькими измерениями. И затем он берет ответ из кортежа, предоставляя вам простой массив. С уплощением он не имеет особого случая для 1-го массива.

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

@Дивакар предлагает np.argwhere:

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

который делает 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,=, transpose, array и т.д.).

Ответ 2

Короткий ответ: np.where предназначен для обеспечения согласованного вывода независимо от размера массива.

Двумерный массив имеет два индекса, поэтому результатом np.where является кортеж длиной 2, содержащий соответствующие индексы. Это обобщается на 3-х кортеж для 3-мерных размеров, 4-х кортежей для 4-х размеров или кортеж длины N для N измерений. По этому правилу ясно, что в 1 измерении результат должен быть длиной 1 кортеж.

Ответ 3

Просто используйте 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]])