Индексируйте массив 2D Numpy с 2 списками индексов

У меня странная ситуация.

У меня есть 2D-массив Numpy, x:

x = np.random.random_integers(0,5,(20,8))

И у меня есть 2 индексатора - один с индексами для строк и один с индексами для столбца. Чтобы индексировать X, мне нужно сделать следующее:

row_indices = [4,2,18,16,7,19,4]
col_indices = [1,2]
x_rows = x[row_indices,:]
x_indexed = x_rows[:,column_indices]

Вместо просто:

x_new = x[row_indices,column_indices]

(который не с ошибкой: не может транслировать (20,) с (2,))


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


Тестовый пример:

x = np.random.random_integers(0,5,(20,8))

row_indices = [4,2,18,16,7,19,4]
col_indices = [1,2]
x_rows = x[row_indices,:]
x_indexed = x_rows[:,col_indices]

x_doesnt_work = x[row_indices,col_indices]

Ответ 1

Выборы или назначения с np.ix_ с индексированием или логическими массивами/масками

1. С indexing-arrays

А. Выбор

Мы можем использовать np.ix_ для получения np.ix_ индексных массивов, которые могут транслироваться друг против друга, что приводит к многомерным комбинациям индексов. Таким образом, когда этот кортеж используется для индексации во входном массиве, мы получим тот же самый многомерный массив. Следовательно, чтобы сделать выборку на основе двух 1D индексных массивов, было бы:

x_indexed = x[np.ix_(row_indices,col_indices)]

Б. Назначение

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

x[np.ix_(row_indices,col_indices)] = # scalar or broadcastable array

2. с masks

Мы также можем использовать логические массивы/маски с np.ix_, аналогично тому, как используются индексные массивы. Это можно использовать снова, чтобы выбрать блок из входного массива, а также для назначений в нем.

А. Выбор

Таким образом, с булевыми массивами row_mask и col_mask в качестве масок для выбора строк и столбцов соответственно, мы можем использовать следующее для выбора:

x[np.ix_(row_mask,col_mask)]

Б. Назначение

И следующие работы для заданий -

x[np.ix_(row_mask,col_mask)] = # scalar or broadcastable array

Пробные прогоны

Использование np.ix_ с indexing-arrays

Входной массив и индексные массивы -

In [221]: x
Out[221]: 
array([[17, 39, 88, 14, 73, 58, 17, 78],
       [88, 92, 46, 67, 44, 81, 17, 67],
       [31, 70, 47, 90, 52, 15, 24, 22],
       [19, 59, 98, 19, 52, 95, 88, 65],
       [85, 76, 56, 72, 43, 79, 53, 37],
       [74, 46, 95, 27, 81, 97, 93, 69],
       [49, 46, 12, 83, 15, 63, 20, 79]])

In [222]: row_indices
Out[222]: [4, 2, 5, 4, 1]

In [223]: col_indices
Out[223]: [1, 2]

Кортеж индексных массивов с помощью np.ix_ -

In [224]: np.ix_(row_indices,col_indices) # Broadcasting of indices
Out[224]: 
(array([[4],
        [2],
        [5],
        [4],
        [1]]), array([[1, 2]]))

Сделайте выбор -

In [225]: x[np.ix_(row_indices,col_indices)]
Out[225]: 
array([[76, 56],
       [70, 47],
       [46, 95],
       [76, 56],
       [92, 46]])

Как предложено OP, это в действительности то же самое, что выполнение вещания старой школы с версией двумерного массива row_indices, элементы/индексы которого отправляются по axis=0 и, таким образом, создается одноэлементное измерение на axis=1 что позволяет осуществлять вещание с col_indices, Таким образом, у нас было бы альтернативное решение, как так -

In [227]: x[np.asarray(row_indices)[:,None],col_indices]
Out[227]: 
array([[76, 56],
       [70, 47],
       [46, 95],
       [76, 56],
       [92, 46]])

Как уже говорилось ранее, для заданий мы просто делаем это.

Row, col, индексация массивов -

In [36]: row_indices = [1, 4]

In [37]: col_indices = [1, 3]

Делать задания со скаляром -

In [38]: x[np.ix_(row_indices,col_indices)] = -1

In [39]: x
Out[39]: 
array([[17, 39, 88, 14, 73, 58, 17, 78],
       [88, -1, 46, -1, 44, 81, 17, 67],
       [31, 70, 47, 90, 52, 15, 24, 22],
       [19, 59, 98, 19, 52, 95, 88, 65],
       [85, -1, 56, -1, 43, 79, 53, 37],
       [74, 46, 95, 27, 81, 97, 93, 69],
       [49, 46, 12, 83, 15, 63, 20, 79]])

Делать назначения с 2D блоком (транслируемый массив) -

In [40]: rand_arr = -np.arange(4).reshape(2,2)

In [41]: x[np.ix_(row_indices,col_indices)] = rand_arr

In [42]: x
Out[42]: 
array([[17, 39, 88, 14, 73, 58, 17, 78],
       [88,  0, 46, -1, 44, 81, 17, 67],
       [31, 70, 47, 90, 52, 15, 24, 22],
       [19, 59, 98, 19, 52, 95, 88, 65],
       [85, -2, 56, -3, 43, 79, 53, 37],
       [74, 46, 95, 27, 81, 97, 93, 69],
       [49, 46, 12, 83, 15, 63, 20, 79]])

Использование np.ix_ с masks

Входной массив -

In [19]: x
Out[19]: 
array([[17, 39, 88, 14, 73, 58, 17, 78],
       [88, 92, 46, 67, 44, 81, 17, 67],
       [31, 70, 47, 90, 52, 15, 24, 22],
       [19, 59, 98, 19, 52, 95, 88, 65],
       [85, 76, 56, 72, 43, 79, 53, 37],
       [74, 46, 95, 27, 81, 97, 93, 69],
       [49, 46, 12, 83, 15, 63, 20, 79]])

Ввод строки, масок col -

In [20]: row_mask = np.array([0,1,1,0,0,1,0],dtype=bool)

In [21]: col_mask = np.array([1,0,1,0,1,1,0,0],dtype=bool)

Сделайте выбор -

In [22]: x[np.ix_(row_mask,col_mask)]
Out[22]: 
array([[88, 46, 44, 81],
       [31, 47, 52, 15],
       [74, 95, 81, 97]])

Делать задания со скаляром -

In [23]: x[np.ix_(row_mask,col_mask)] = -1

In [24]: x
Out[24]: 
array([[17, 39, 88, 14, 73, 58, 17, 78],
       [-1, 92, -1, 67, -1, -1, 17, 67],
       [-1, 70, -1, 90, -1, -1, 24, 22],
       [19, 59, 98, 19, 52, 95, 88, 65],
       [85, 76, 56, 72, 43, 79, 53, 37],
       [-1, 46, -1, 27, -1, -1, 93, 69],
       [49, 46, 12, 83, 15, 63, 20, 79]])

Делать назначения с 2D блоком (транслируемый массив) -

In [25]: rand_arr = -np.arange(12).reshape(3,4)

In [26]: x[np.ix_(row_mask,col_mask)] = rand_arr

In [27]: x
Out[27]: 
array([[ 17,  39,  88,  14,  73,  58,  17,  78],
       [  0,  92,  -1,  67,  -2,  -3,  17,  67],
       [ -4,  70,  -5,  90,  -6,  -7,  24,  22],
       [ 19,  59,  98,  19,  52,  95,  88,  65],
       [ 85,  76,  56,  72,  43,  79,  53,  37],
       [ -8,  46,  -9,  27, -10, -11,  93,  69],
       [ 49,  46,  12,  83,  15,  63,  20,  79]])

Ответ 2

Как насчет:

x[row_indices][:,col_indices]

Например,

x = np.random.random_integers(0,5,(5,5))
## array([[4, 3, 2, 5, 0],
##        [0, 3, 1, 4, 2],
##        [4, 2, 0, 0, 3],
##        [4, 5, 5, 5, 0],
##        [1, 1, 5, 0, 2]])

row_indices = [4,2]
col_indices = [1,2]
x[row_indices][:,col_indices]
## array([[1, 5],
##        [2, 0]])

Ответ 3

import numpy as np
x = np.random.random_integers(0,5,(4,4))
x
array([[5, 3, 3, 2],
       [4, 3, 0, 0],
       [1, 4, 5, 3],
       [0, 4, 3, 4]])

# This indexes the elements 1,1 and 2,2 and 3,3
indexes = (np.array([1,2,3]),np.array([1,2,3]))
x[indexes]
# returns array([3, 5, 4])

Обратите внимание, что numpy имеет очень разные правила в зависимости от того, какие индексы вы используете. Поэтому индексирование нескольких элементов должно быть tuple np.ndarray (см. руководство по индексированию).

Итак, вам нужно только преобразовать ваш list в np.ndarray, и он должен работать как ожидалось.

Ответ 4

Я думаю, что вы пытаетесь выполнить одно из следующих (equlvalent) операций:

x_does_work = x[row_indices,:][:,col_indices]
x_does_work = x[:,col_indices][row_indices,:]

Это фактически создаст подмножество x только с выбранными строками, затем выберите столбцы из этого или наоборот во втором случае. Первый случай можно рассматривать как

x_does_work = (x[row_indices,:])[:,col_indices]