Почему результат другой? Numpy Нарезка и индексирование

По сути, я хочу получить часть переменной «cubote». Я попробовал два метода, которые должны работать одинаково, но это не помогло.

Мой код:

   import numpy as np

   # Create a 3x3x3 cube
   cubote = np.arange(27).reshape(3,3,3)

   # Compare the results
   result1 = cubote[0:2,0:2,0:2]
   result2 = cubote[0:2][0:2][0:2]

   print(result1)
   print("Shape of result1:", result1.shape)

   print(result2)
   print("Shape of result2:", result2.shape)

ВЫХОД:

результат1:

   [[[ 0  1]
     [ 3  4]]

    [[ 9 10]
     [12 13]]]
   Shape of result1: (2, 2, 2)

результат2:

   [[[ 0  1  2]
     [ 3  4  5]
     [ 6  7  8]]

    [[ 9 10 11]
     [12 13 14]
     [15 16 17]]]
   Shape of result2: (2, 3, 3)

Я ожидал, что оба результата будут одинаковыми.

Я говорю о том, что результат 1 имеет смысл, но результат 2 не сработал так, как я ожидал.

ПОЧЕМУ?

numpy не меняет синтаксис Python или последовательность операций. Таким образом, каждая операция индексирования [...] представляет собой отдельный вызов Python. Поэтому вам нужно понять, что делает каждый. [2] отличается от [0:2].
hpaulj 05.09.2024 19:04
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
1
95
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

cubote[0:2] — это массив фигур (2, 3, 3). Если вы добавите дополнительные [0:2] в этот массив, вы снова и снова будете брать первые 2 (то есть все) элемента с первой оси этого (2, 3, 3) массива. Так что ничего не изменится. Даже cubote[0:2][0:2][0:2][0:2][0:2][0:2][0:2] даст тот же результат.

Ответ принят как подходящий

Разница в результатах между result1 и result2 сводится к тому, как работает индексация в NumPy.

Объяснение результата 1:

result1 = cubote[0:2, 0:2, 0:2]

В этом случае вы применяете нарезку по всем трем измерениям одновременно. Это означает, что вы извлекаете субкуб с индексами в диапазоне [0:2] во всех трех измерениях, что дает вам куб 2x2x2.

Объяснение результата2:

result2 = cubote[0:2][0:2][0:2]

Здесь вы делаете что-то другое. Давайте разберем это:

1.cubote[0:2] извлекает из куба первые два «слоя» (2D-массивы), поэтому результат этого шага:

array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8]],

       [[ 9, 10, 11],
        [12, 13, 14],
        [15, 16, 17]]])

Это массив 2х3х3.

  1. Затем к этому результату применяется [0:2], что дополнительно извлекает первые две строки (вдоль первой оси) нового массива. Но поскольку массив уже имеет размер 2х3х3, размер от этого не уменьшается, и у вас все равно остается массив 2х3х3.

  2. Наконец, [0:2] применяется снова, но поскольку теперь вы работаете с первой осью текущих 2D-срезов (каждый имеет форму 3x3), оно ведет себя не так, как ожидалось, потому что вы уже «отслоились» размеры.

Ключевое отличие:

  1. Разрез по всем измерениям одновременно (как в результате 1) извлекает правильный подкуб.

  2. Цепная индексация (как в result2) выполняет индексацию последовательно, и поскольку каждый срез индекса работает с текущим результатом, а не со всеми измерениями одновременно, вы не получите одинаковую форму или результат.

Вам следует использовать нарезку во всех измерениях одновременно, как в примере result1, чтобы получить ожидаемый субкуб 2x2x2.

cubote[0:2, 0:2, 0:2]: извлекает субкуб по всем трем измерениям.

cubote[0:2][0:2][0:2]: последовательно извлекает срезы, что приводит к неожиданному результату.

Чтобы получить согласованные результаты, всегда применяйте срезы по всем измерениям одновременно, если вы пытаетесь извлечь подмассив.

В дополнение к ответам Стефа и BlueLightning, которые уже прекрасно освещают актуальный вопрос, я хотел бы пролить свет на то, откуда, вероятно, возникает ваше заблуждение («я попробовал два метода, которые должны работать одинаково»). от:

Последовательное индексирование, выполненное таким же образом, как и последовательная нарезка для result2, действительно приведет к ожидаемому результату. Возьмем, к примеру:

import numpy as np

cubote = np.arange(3*4*5).reshape(3, 4, 5)

print("indexing_result_1:", cubote[1, 2, 4])
# >>> indexing_result_1: 34
print("indexing_result_2:", cubote[1][2][4])
# >>> indexing_result_2: 34

При последовательном индексировании происходит то, что с каждым примененным индексом соответствующее измерение (или «ось» в NumPy) будет удалено:

  1. Результатом cubote[1] является двумерный массив формы (4, 5).
  2. Результатом cubote[1][2] является одномерный массив формы (5,).
  3. Результатом cubote[1][2][4] является скаляр.

Благодаря последовательному удалению измерений, последовательные значения индекса будут применены к тем же самым (оставшимся) измерениям, как это делается со всеми значениями индекса в cubote[1, 2, 4] в одном и том же вызове.

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

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

Это означает, что нужно нарезать [0:2], затем нарезать второе измерение [:, 0:2], затем третье [:, :, 0:2]:

result2 = cubote[0:2][:, 0:2][:, :, 0:2]
print(result2)
print("Shape of result2:", result2.shape)

Выход:

[[[ 0  1]
  [ 3  4]]

 [[ 9 10]
  [12 13]]]
Shape of result2: (2, 2, 2)

Конечно, нарезать все сразу (cubote[0:2, 0:2, 0:2]) удобнее (а также более эффективно, поскольку вы не создаете промежуточные элементы).

%timeit cubote[0:2,0:2,0:2]
232 ns ± 1.08 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

%timeit cubote[0:2][:, 0:2][:, :, 0:2]
555 ns ± 1.35 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

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