#python #numpy #slice
Вопрос:
Цель состоит в том, чтобы срезать 3D-массив, используя список индексов.
Здесь массив имеет форму 2,5,5
. Для простоты предположим, что индекс от 0 до 4 обозначен как A,B,C,D,E
.
Предположим, что у нас есть 3d-массив, как показано ниже
array([[[44, 47, 64, 67, 67],
[ 9, 83, 21, 36, 87],
[70, 88, 88, 12, 58],
[65, 39, 87, 46, 88],
[81, 37, 25, 77, 72]],
[[ 9, 20, 80, 69, 79],
[47, 64, 82, 99, 88],
[49, 29, 19, 19, 14],
[39, 32, 65, 9, 57],
[32, 31, 74, 23, 35]]], dtype=int64)
Индекс интереса таков [1,3,4]
. Опять же, мы обозначаем это как B,D,E`. Ожидаемый результат при разрезании 3D-массива на основе индекса выглядит следующим образом
array([[[83, 36, 87],
[39, 46, 88],
[37, 77, 72]],
[[64, 99, 88],
[32, 9, 57],
[31, 23, 35]]], dtype=int64)
Однако, разрезая массив, как показано ниже
import numpy as np
np.random.seed(0)
arr = np.random.randint(0, 100, size=(2, 5, 5))
k=arr[:,(1,3,4),(1,3,4)]
не дает ожидаемого результата.
В фактическом случае использования количество элементов, подлежащих нарезке, составляет > 3 элемента (>> B,D,E). Извините за отсутствие правильной используемой терминологии
Ответ №1:
Попробуйте это, структура которого аналогична вашей arr[:,idx,idx]
, но используется np.ix_()
. Прочтите документацию для np.ix().-
idx = [1,3,4]
ixgrid = np.ix_(idx,idx)
arr[:,ixgrid[0],ixgrid[1]]
array([[[83, 36, 87],
[39, 46, 88],
[37, 77, 72]],
[[64, 99, 88],
[32, 9, 57],
[31, 23, 35]]])
Объяснение
То, что вы ХОТИТЕ сделать, — это извлечь сетку из последних 2 осей массива. Но то, что вы делаете, — это извлекаете точные индексы из каждой из 2 осей.
- Когда вы используете
arr[:,(1,3,4),(1,3,4)]
, вы , по сути, просите(1,1)
,(3,3)
и(4,4)
из двух матрицarr[0]
иarr[1]
- Что вам нужно, так это извлечь сетку. Этого можно достичь с
np.ix_
помощью магии вещания.
Если вы попросите …
[[1],
[3], and [1,3,4]
[4]]
… вот что такое np.ix_
конструкции, вы транслируете индексы и вместо этого просите перекрестный продукт между ними, который (1,1), (1,3), (1,4), (3,1), (3,3)...
и т. Д.
Надеюсь, это прояснит, почему вы получаете тот результат, который получаете, и как вы на самом деле можете получить то, что вам нужно.
Ответ №2:
Проблема
Расширенная индексация предполагает, что все измерения будут проиндексированы явно. То, что вы здесь делаете, — это захват элементов в координатах (1, 1), (3, 3), (4, 4)
в каждом массиве вдоль оси 0.
Решение
Вместо этого вам нужно сделать вот что:
idx = (1, 3, 4) # the indices of interest
arr[np.ix_((0, 1), idx, idx)]
Где (0, 1)
соответствует первым двум массивам вдоль оси 0.
Выход:
array([[[83, 36, 87],
[39, 46, 88],
[37, 77, 72]],
[[64, 99, 88],
[32, 9, 57],
[31, 23, 35]]], dtype=int64)
Как показано выше, np.ix_((0, 1), idx, idx))
создается объект, который можно использовать для расширенной индексации. Это (0, 1)
означает, что вы явно выбираете элементы из массивов arr[0]
и arr[1]
. Если у вас есть более общий 3D-массив формы (n, m, q)
и вы хотите захватить один и тот же подмассив из каждого массива вдоль оси 0, вы можете использовать
np.ix_(np.arange(arr.shape[0]), idx, idx))
Как ваши показатели. Обратите внимание, что idx
это повторяется здесь, потому что вам нужны были эти конкретные индексы, но в целом они не должны совпадать.
Обобщая
В более общем плане, вы можете нарезать и нарезать кубиками, как вам заблагорассудится, вот так:
In [1]: arrays_to_select = (0, 1)
In [2]: rows_to_select = (1, 3, 4)
In [3]: cols_to_select = (1, 3, 4)
In [4]: indices = np.ix_(arrays_to_select, rows_to_select, cols_to_select)
In [5]: arr[indices]
Out[5]:
array([[[83, 36, 87],
[39, 46, 88],
[37, 77, 72]],
[[64, 99, 88],
[32, 9, 57],
[31, 23, 35]]], dtype=int64)
Давайте рассмотрим какую-нибудь другую форму:
In [4]: x = np.random.randint(0, 9, (4, 3, 5))
In [5]: x
Out[5]:
array([[[1, 0, 2, 1, 0],
[3, 5, 1, 4, 3],
[1, 8, 1, 4, 2]],
[[1, 6, 8, 2, 8],
[0, 0, 4, 2, 3],
[8, 5, 6, 2, 5]],
[[4, 4, 8, 6, 0],
[3, 0, 1, 2, 8],
[0, 8, 2, 4, 3]],
[[7, 8, 8, 1, 4],
[5, 7, 4, 8, 5],
[7, 5, 5, 3, 4]]])
In [6]: rows = (0, 2)
In [7]: cols = (0, 2, 3, 4)
Используя эти rows
и cols
, вы будете захватывать подмассивы, состоящие из всех элементов из столбцов с 0 по 4, только из строк 0 и 2. Давайте проверим это с помощью первого массива вдоль оси 0:
In [8]: arrs = (0,) # A 1-tuple which will give us only the first array along axis 0
In [9]: x[np.ix_(arrs, rows, cols)]
Out[9]:
array([[[1, 2, 1, 0],
[1, 1, 4, 2]]])
Теперь предположим, что вы хотите, чтобы подмассивы создавались rows
и cols
состояли только из первого и последнего массивов вдоль оси 0. Вы можете явно выбрать (0, -1)
:
In [10]: arrs = (0, -1)
In [11]: x[np.ix_(arrs, rows, cols)]
Out[11]:
array([[[1, 2, 1, 0],
[1, 1, 4, 2]],
[[7, 8, 1, 4],
[7, 5, 3, 4]]])
Если вместо этого вам нужен один и тот же подмассив из всех массивов вдоль оси 0:
In [12]: arrs = np.arange(x.shape[0])
In [13]: arrs
Out[13]: array([0, 1, 2, 3])
In [14]: x[np.ix_(arrs, rows, cols)]
Out[14]:
array([[[1, 2, 1, 0],
[1, 1, 4, 2]],
[[1, 8, 2, 8],
[8, 6, 2, 5]],
[[4, 8, 6, 0],
[0, 2, 4, 3]],
[[7, 8, 1, 4],
[7, 5, 3, 4]]])