Нарезка 2d-массива NumPy или как извлечь подматрицу mxm из массива nxn (n>m)?
Я хочу нарезать массив NumPy nxn. Я хочу извлечь произвольные выбор m строк и столбцов этого массива (т. е. без какого-либо шаблона в количестве строк/столбцов), что делает его новым массивом mxm. Для этого примера предположим, что массив равен 4x4, и я хочу извлечь из него массив 2x2.
вот наш массив:
from numpy import *
x = range(16)
x = reshape(x,(4,4))
print x
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
строка и столбцы для удаления одинаковы. Самый простой случай, когда я хочу извлечь подматрицу 2x2, которая находится в начало или конец, то есть:
In [33]: x[0:2,0:2]
Out[33]:
array([[0, 1],
[4, 5]])
In [34]: x[2:,2:]
Out[34]:
array([[10, 11],
[14, 15]])
но что если мне нужно удалить другую смесь строк/столбцов? Что делать, если мне нужно удалить первую и третью строки/строки, тем самым извлекая подматрицу [[5,7],[13,15]]
? Может быть любая композиция строк / строк. Я где-то читал, что мне просто нужно индексировать мой массив, используя массивы/списки индексов для строк и столбцов, но это, похоже, не работает:
In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])
я нашел один способ, которым является:
In [61]: x[[1,3]][:,[1,3]]
Out[61]:
array([[ 5, 7],
[13, 15]])
первый вопрос с этим то, что это трудно читается, хотя я могу жить с этим. Если у кого-то есть лучшее решение, я бы хотел его услышать.
другое дело, что я читаю на форуме что индексирование массивов с массивами заставляет NumPy делать копию нужного массива, поэтому при обработке с большими массивами это может стать проблемой. Почему это так и как работает этот механизм?
7 ответов
как Sven сказано, x[[[0],[2]],[1,3]]
вернет 0 и 2 строки, которые соответствуют 1 и 3 столбцам в то время как x[[0,2],[1,3]]
вернет значения x[0,1] и x[2,3] в массиве.
существует полезная функция для выполнения первого примера, который я дал,numpy.ix_
. Вы можете сделать то же самое, что и мой первый пример с x[numpy.ix_([0,2],[1,3])]
. Это может избавить вас от необходимости вводить все эти дополнительные скобки.
чтобы ответить на этот вопрос, мы должны посмотреть, как индексирование многомерного массива работает в Numpy. Давайте сначала скажем, что у вас есть array x
из вашего вопроса. Буфер, назначенный x
будет содержать 16 целых чисел по возрастанию от 0 до 15. Если у вас есть доступ к одному элементу, скажите x[i,j]
, NumPy должен выяснить местоположение памяти этого элемента относительно начала буфера. Это делается путем вычисления in effect i*x.shape[1]+j
(и умножение с размером int, чтобы получить фактическое смещение памяти).
если вы извлекаете subarray путем основной нарезки, как y = x[0:2,0:2]
, результирующий объект будет делиться буфера с x
. Но что произойдет, если вы Acces y[i,j]
? NumPy не может использовать i*y.shape[1]+j
для вычисления смещения в массив, потому что данные, принадлежащие y
не является последовательным в памяти.
NumPy решает эту проблему, вводя успехов. При расчете смещения памяти для доступа x[i,j]
, что на самом деле рассчитывается i*x.strides[0]+j*x.strides[1]
(и это уже включает фактор для размера int):
x.strides
(16, 4)
, когда y
извлекается, как указано выше, NumPy не создает новый буфер, но он тут создайте новый объект массива, ссылающийся на тот же буфер (в противном случае y
будет равна x
.) Новый объект массива будет иметь другую форму, чем x
и, возможно, другое начальное смещение в буфер, но будет делиться шагами с x
(в данном случае по крайней мере):
y.shape
(2,2)
y.strides
(16, 4)
таким образом, вычисление смещения памяти для y[i,j]
даст правильный результат.
но что должен делать NumPy для чего-то вроде z=x[[1,3]]
? Механизм шагов не позволит правильно индексировать, если исходный буфер используется для z
. И NumPy теоретически мог бы добавьте более сложный механизм, чем шаги, но это сделает доступ к элементам относительно дорогим, каким - то образом бросая вызов вся идея массива. Кроме того, представление больше не будет действительно легким объектом.
это освещается в документация NumPy по индексированию.
О, и почти забыл о вашем фактическом вопросе: вот как сделать индексацию с несколькими списками работать так, как ожидалось:
x[[[1],[3]],[1,3]]
это потому, что массивы индексов транслироваться в общей форме. Конечно, для этого конкретного примера, вы также можете обойтись базовой нарезкой:
x[1::2, 1::2]
Я так не думаю x[[1,3]][:,[1,3]]
трудно читается. Если вы хотите быть более ясными в своих намерениях, вы можете сделать:
a[[1,3],:][:,[1,3]]
Я не эксперт в нарезке, но обычно, если вы пытаетесь нарезать массив и значения непрерывны, вы получаете представление, в котором изменяется значение шага.
например, в ваших входах 33 и 34, хотя вы получаете массив 2x2, шаг равен 4. Таким образом, когда вы индексируете следующую строку, указатель перемещается в правильное положение в память.
очевидно, что этот механизм не очень хорошо переносится в случае массива индексов. Следовательно, numpy придется сделать копию. В конце концов, многие другие матричные математические функции зависят от размера, шага и непрерывного выделения памяти.
если вы хотите пропустить каждую другую строку и каждый другой столбец, то вы можете сделать это с помощью basic slicing:
In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]:
array([[ 5, 7],
[13, 15]])
это возвращает представление, а не копию вашего массива.
In [51]: y=x[1:4:2,1:4:2]
In [52]: y[0,0]=100
In [53]: x # <---- Notice x[1,1] has changed
Out[53]:
array([[ 0, 1, 2, 3],
[ 4, 100, 6, 7],
[ 8, 9, 10, 11],
[ 12, 13, 14, 15]])
пока z=x[(1,3),:][:,(1,3)]
использует расширенную индексацию и, таким образом, возвращает копию:
In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]
In [60]: z
Out[60]:
array([[ 5, 7],
[13, 15]])
In [61]: z[0,0]=0
отметим, что x
остается неизменной:
In [62]: x
Out[62]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
если вы хотите выбрать произвольные строки и столбцы, вы не можете использовать базовое нарезание. Вам придется использовать расширенное индексирование, используя что-то вроде x[rows,:][:,columns]
, где rows
и columns
последовательности. Это, конечно, даст вам копию, а не представление вашего исходного массива. Это так, как следует ожидать, поскольку массив numpy использует непрерывную память (с постоянными шагами), и не было бы возможности генерировать представление с произвольными строками и столбцами (так как для этого потребовались бы непостоянные шаги).
С numpy вы можете передать срез для каждого компонента индекса-Итак, ваш x[0:2,0:2]
пример выше работает.
если вы просто хотите равномерно пропустить столбцы или строки, вы можете передать срезы с тремя компонентами (т. е. start, stop, step).
опять же, для вашего примера выше:
>>> x[1:4:2, 1:4:2]
array([[ 5, 7],
[13, 15]])
что в основном: срез в первом измерении, начиная с индекса 1, останавливается, когда индекс равен или больше 4, и добавляет 2 к индексу в каждом проходе. То же самое для второе измерение. Опять же: это работает только для постоянных шагов.
синтаксис, вы должны сделать что-то совсем другое внутренне - то x[[1,3]][:,[1,3]]
фактически создает новый массив, включающий только строки 1 и 3 из исходного массива (сделано с x[[1,3]]
part), а затем повторно срежьте это-создание третьего массива - включая только столбцы 1 и 3 предыдущего массива.
у меня есть аналогичный вопрос здесь:запись в суб-ndarray в ndarray в самых pythonian пути. В Python 2 .
после решения предыдущего сообщения для вашего случая решение выглядит следующим образом:
columns_to_keep = [1,3]
rows_to_keep = [1,3]
С помощью ix_:
x[np.ix_(rows_to_keep, columns_to_keep)]
что:
array([[ 5, 7],
[13, 15]])
Я не уверен, насколько это эффективно, но вы можете использовать range () для среза на обеих осях
x=np.arange(16).reshape((4,4))
x[range(1,3), :][:,range(1,3)]