#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. Классно! Спасибо!!