Индексирование NumPy: трансляция с логическими массивами - PullRequest
0 голосов
/ 04 июля 2018

Относительно к этому вопросу я столкнулся с поведением индексации через логические массивы и широковещание, которое я не понимаю. Мы знаем, что можно индексировать массив NumPy в двух измерениях, используя целочисленные индексы и широковещательную рассылку. Это указано в документах :

a = np.array([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11]])

b1 = np.array([False, True, True])
b2 = np.array([True, False, True, False])

c1 = np.where(b1)[0]  # i.e. [1, 2]
c2 = np.where(b2)[0]  # i.e. [0, 2]

a[c1[:, np.newaxis], c2]  # or a[c1[:, None], c2]

array([[ 4,  6],
       [ 8, 10]])

Однако то же самое не работает для логических массивов.

a[b1[:, None], b2]

IndexError: too many indices for array

Альтернатива numpy.ix_ работает как для логических массивов , так и для . Похоже, это связано с тем, что ix_ выполняет специальные манипуляции для логических массивов, чтобы обеспечить согласованное лечение.

assert np.array_equal(a[np.ix_(b1, b2)], a[np.ix_(c1, c2)])

array([[ 4,  6],
       [ 8, 10]])

Итак, мой вопрос: почему вещание работает с целыми числами, а не с булевыми массивами? Это поведение задокументировано? Или я неправильно понимаю более фундаментальную проблему?

1 Ответ

0 голосов
/ 04 июля 2018

Как отмечается в комментариях @ Divakar , булевы расширенные индексы ведут себя так, как если бы они сначала передавались через np.nonzero, а затем передавались вместе, см. Соответствующую документацию объяснения . Цитируя документы,

Как правило, если индекс включает в себя логический массив, результат будет идентичен вставке obj.nonzero() в ту же позицию и использованию механизма индексации целочисленного массива, описанного выше. x[ind_1, boolean_array, ind_2] эквивалентно x[(ind_1,) + boolean_array.nonzero() + (ind_2,)].
[...]
Объединение нескольких массивов логического индексирования или логического массива с массивом целочисленного индексирования лучше всего понять по аналогии obj.nonzero(). Функция ix_ также поддерживает логические массивы и будет работать без сюрпризов.

В вашем случае вещание не обязательно будет проблемой, поскольку оба массива имеют только два ненулевых элемента. Проблема заключается в количестве измерений в результате:

>>> len(b1[:,None].nonzero())
2
>>> len(b2.nonzero())
1

Следовательно, индексное выражение a[b1[:,None], b2] будет эквивалентно a[b1[:,None].nonzero() + b2.nonzero()], что поместит кортеж длины 3 внутрь a, что соответствует индексу трехмерного массива. Отсюда и ошибка, которую вы видите по поводу «слишком много индексов».

Сюрпризы, упомянутые в документах, очень близки вашему примеру: что, если вы не внедрили это одноэлементное измерение? Начиная с логического массива длины 3 и длины 4 вы получите расширенный индекс длины 2, то есть массив 1d размером (2,). Это никогда не то, что вы хотели бы, что приводит нас к еще одной мелочи в теме.

Было много дискуссий о планировании модернизации расширенной индексации, см. Незавершенный проект NEP 21 . Суть проблемы заключается в том, что причудливое индексирование в numpy, хотя и четко задокументировано, имеет некоторые очень причудливые функции, которые практически ни к чему не полезны, но которые могут укусить вас, если вы совершите ошибку, выдавая удивительные результаты, а не ошибки.

Соответствующая цитата из нэпа:

Смешанные случаи, включающие несколько индексов массива, также удивительны, только менее проблематично, потому что текущее поведение настолько бесполезно, что это редко встречается на практике. Когда индекс логического массива смешанный с другим логическим или целочисленным массивом, логический массив преобразуются в индексы целочисленных массивов (эквивалентные np.nonzero()) и потом вещание. Например, индексирование двумерного массива размером (2, 2), например x[[True, False], [True, False]] создает 1D вектор с формой (1,), не 2D подматрица с формой (1, 1).

Теперь я подчеркиваю, что NEP находится в стадии разработки, но одно из предложений в текущем состоянии NEP состоит в том, чтобы запретить логические массивы в случаях расширенного индексирования, таких как приведенные выше, и разрешать их только в сценарии «внешнего индексирования», т. е. именно то, что np.ix_ поможет вам с вашим логическим массивом:

Логическое индексирование - это концептуально внешнее индексирование. Вещание вместе с другими передовыми индексами в виде унаследованной индексации [т.е. текущее поведение], как правило, не является полезным или четко определенным. Таким образом, можно ожидать, что пользователь, который желает, чтобы поведение «отличное от нуля» и «широковещание» делало это вручную.

Моя точка зрения заключается в том, что поведение булевых расширенных индексов и их устаревший статус (или их отсутствие) могут измениться в не столь отдаленном будущем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...