Нарезка 3D-массива numpy с использованием списка индексов

#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]]])