Numpy: доступ к значениям многомерного массива на основе списка индексов

#python #numpy

Вопрос:

Допустим, у меня есть многомерный массив:

 np.array([[1, 0, 0], [0, 0, 1]])
 

И я хочу извлечь значения из дополнительного списка индексов:

 np.array([0, 2])
 

Где ожидаемый результат равен:

 [1, 1]
 

Как лучше всего подойти к этому?

Ответ №1:

Здесь,

 >>> desired_cols = np.array([0, 2])
>>> desired_rows = np.arange(len(desired_cols))
>>> x[desired_rows, desired_cols]
array([1, 1])
 

Вы np.array([0, 2]) не предоставляете достаточно информации для индексации в многомерный массив. Здесь я предполагаю, основываясь на вашем примере, что это те столбцы, которые вы хотите выбрать. Поскольку вам также необходимо указать соответствующие строки, которые вы хотите выбрать, я создал arange их на основе длины нужных столбцов.

Выбор отдельных элементов

Как правило, для расширенной индексации требуются списки индексов для каждой оси:

 x[[axis_0_idxs], [axis_1_idxs], ...]
 

Где , если бы вы это сделали zip(axis_0_idxs, axis_1_idxs, ...) , вы бы создали кортежи координат. Например, с индексами, используемыми для вашей проблемы:

 >>> list(zip(desired_rows, desired_columns))
[(0, 0), (1, 2)]
 

Выбор подзадач

Однако, если вы хотите выбрать ВСЕ значения из нужных строк вместе со ВСЕМИ значениями из нужных столбцов, вы можете использовать np.ix_() . Вот более сложный пример:

 >>> x = np.random.randint(0, 9, (5, 5), dtype="uint8")
>>> x
array([[87, 57, 64, 48, 15],
       [72,  8,  0, 81, 63],
       [63, 51, 66,  0, 68],
       [77, 46, 74, 74, 86],
       [51, 59, 48, 81, 75]], dtype=uint8)
 

Предположим, мы хотим, чтобы подмножество соответствовало строкам 1, 2 и 3, а столбцы 0, 2 и 4. Используя базовые списки для индексации x , мы вместо этого получаем массив из трех элементов:

 >>> rows = [1, 2, 3]
>>> cols = [0, 2, 4]
>>> x[rows, cols]
array([72, 66, 86], dtype=uint8)
 

Это связано с тем, что мы используем списки 1D, которые, опять же, по сути, объединены в кортежи координат. Если мы хотим выбрать подмассив, состоящий из строк 1, 2, 3 и столбцов 0, 2, 4, нам нужно выбрать все столбцы для каждой из строк. Это, в некотором смысле, декартово произведение строк и столбцов, но поскольку декартово произведение все равно будет производить только 1D-последовательность кортежей координат, мы все равно получим только 1D-результат, даже если получим правильные значения.

Но с помощью np.ix_() этого мы получаем сетку координат , представленную очень компактно:

 >>> np.ix_(rows, cols)
(array([[1],
        [2],
        [3]]),
 array([[0, 2, 4]]))
 

Используя это для индексирования, мы получаем 3x3 нужный нам подмассив:

 >>> x[np.ix_(rows, cols)]
array([[72,  0, 63],
       [63, 66, 68],
       [77, 74, 86]], dtype=uint8)
 

Вот немного чистого Python, чтобы продемонстрировать, как ведет себя индексирование с np.ix_ помощью объекта:

 >>> all_rows = [[r]*len(cols) for r in rows]

>>> all_cols = [cols]*len(rows)

>>> all_rows
[[1, 1, 1], [2, 2, 2], [3, 3, 3]]

>>> all_cols
[[0, 2, 4], [0, 2, 4], [0, 2, 4]]

>>> x[all_rows, all_cols]
array([[72,  0, 63],
       [63, 66, 68],
       [77, 74, 86]], dtype=uint8)
 

Обратите внимание, что all_rows и all_cols являются 2D-списками. Обратите также внимание, что это гораздо более утомительно и подвержено ошибкам (умножение столбцов на количество строк, строк на количество столбцов, какой из них повторять по элементам, какой из них повторять по подспискам и т. Д.).

Еще одним приятным преимуществом использования np.ix_() является то, что мы можем очень легко выбирать неквадратные подмассивы, не беспокоясь о головной боли, связанной с подходом pure Python:

 >>> x[np.ix_([1, 2], [0, 1, 3, 4])]
array([[72,  8, 81, 63],
       [63, 51,  0, 68]], dtype=uint8)
 

Комментарии:

1. Классно! Спасибо!!