Как понять следующее причудливое поведение индекса для многомерных массивов?

Мы заметили, что смешанное использование причудливой индексации и нарезки настолько запутанно и недокументировано для многомерных массивов, например:

In [114]: x = np.arange(720).reshape((2,3,4,5,6))

In [115]: x[:,:,:,0,[0,1,2,4,5]].shape
Out[115]: (2, 3, 4, 5)

In [116]: x[:,:,0,:,[0,1,2,4,5]].shape
Out[116]: (5, 2, 3, 5)

Я прочитал об использовании причудливого индексирования на https://numpy.org/doc/stable/user/basics.indexing.html и могу это понять x[:,0,:,[1,2]] = [x[:,0,:,1], x[:,0,:,2]]. Однако я не могу понять, почему результат для приведенных выше Input [115] и Input [116] различается по первому измерению. Может ли кто-нибудь указать, где задокументированы такие правила вещания?

Спасибо!

Я пытался найти документацию для причудливой индексации, а также публиковать проблемы в репозитории numpy на Github.

Из-за улучшений в документации мы должны сопротивляться попыткам пометить этот вопрос как дубликат.

hpaulj 14.06.2023 11:56
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
5
1
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В операции индексирования есть две части: подпространство, определяемое базовой индексацией (исключая целые числа), и подпространство из расширенной части индексации. Следует различать два случая комбинации индексов:

  • Расширенные индексы разделены срезом, многоточием или новой осью. Например, x[обр1, :, обр2].
  • Все расширенные индексы расположены рядом друг с другом. Например, x[..., arr1, arr2, :], но не x[arr1, :, 1], поскольку 1 в этом отношении является расширенным индексом.

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

Из https://numpy.org/doc/stable/user/basics.indexing.html#combining-advanced-and-basic-indexing

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

Спасибо за указание на документацию. Кстати, вы знаете, почему numpy приводит к такому нелогичному поведению? Я пробовал Pytorch, и он ведет себя по-другому (но его легко понять) с Numpy.

sighingnow 14.06.2023 11:34

Документация по смешанному расширенному и базовому уровню является новой, так как в последний раз я пытался ответить на этот вопрос. В прошлом просто говорилось, что из-за двусмысленности размеры Алисы помещаются последними.

hpaulj 14.06.2023 11:50

«потому что в подпространстве индексации нет однозначного места для перетаскивания, поэтому оно прикрепляется к началу. Всегда можно использовать .transpose() для перемещения подпространства в любое место». Очевидно, в процессе есть двусмысленность, но я не могу ее найти.

rochard4u 14.06.2023 14:28

@ rochard4u, неоднозначность становится более очевидной, когда срез разделяет массивы расширенного индексирования.

hpaulj 14.06.2023 15:44
Ответ принят как подходящий

Некоторое дополнительное понимание того, почему существует двусмысленность:

В последнем случае в вопросе 3-я и 5-я оси индексируются и, таким образом, исчезают из нового массива. Где-то нужно добавить новую ось (с формой, равной трансляции индексов). Если бы я был пустым и мне нужно было бы вставить массив формы (5,) в массив с «формой» (2, 3, -, 5, -), поместил бы я его вместо первого отсутствующего измерения? Или второй?

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

(5, 2, 3, 5)
 ^  ^^^^^^^--- old dimensions
 |
new dimension

Только в первом случае, когда все исчезающие оси являются смежными («форма» (2, 3, 4, -, -)), новые оси могут быть однозначно вставлены в конце.

Примечание. За кулисами numpy всегда вставляет новые оси в начале. Он просто (в основном для удобства, я полагаю) перемещает массив для перемещения новых осей на место, когда это однозначно.

Также интересно Numpy Enhancement Proposal 21

Спасибо за объяснение двусмысленности здесь.

sighingnow 15.06.2023 06:25

Другие вопросы по теме