По сути, я хочу получить часть переменной «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 не сработал так, как я ожидал.
ПОЧЕМУ?
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.
Затем к этому результату применяется [0:2], что дополнительно извлекает первые две строки (вдоль первой оси) нового массива. Но поскольку массив уже имеет размер 2х3х3, размер от этого не уменьшается, и у вас все равно остается массив 2х3х3.
Наконец, [0:2] применяется снова, но поскольку теперь вы работаете с первой осью текущих 2D-срезов (каждый имеет форму 3x3), оно ведет себя не так, как ожидалось, потому что вы уже «отслоились» размеры.
Ключевое отличие:
Разрез по всем измерениям одновременно (как в результате 1) извлекает правильный подкуб.
Цепная индексация (как в 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) будет удалено:
cubote[1]
является двумерный массив формы (4, 5)
.cubote[1][2]
является одномерный массив формы (5,)
.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)
numpy
не меняет синтаксис Python или последовательность операций. Таким образом, каждая операция индексирования[...]
представляет собой отдельный вызов Python. Поэтому вам нужно понять, что делает каждый.[2]
отличается от[0:2]
.