Нарезка массива numpy 2d или как извлечь подматрицу mxm из массива nxn (n>m)?
Я хочу, чтобы нарезать массив NumPy собой. Я хочу извлечь произвольные выбор 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. Давайте сначала скажем, что у вас есть массив
x
из вашего вопроса. Буфер, назначенныйx
будет содержать 16 целых чисел по возрастанию от 0 до 15. Если вы получаете доступ к одному элементу, скажитеx[i,j]
, NumPy должен выяснить расположение памяти этого элемента относительно начала буфера. Это делается путем вычисления в действииi*x.shape[1]+j
(и умножение с размером int, чтобы получить фактическое смещение памяти).если вы извлекаете подмассив путем базовой нарезки, как
y = x[0:2,0:2]
, результирующий объект будет совместно использовать базовый буфер сx
. Но что произойдет, если вы получите доступ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]
пример выше работает.если вы просто хотите равномерно пропустить столбцы или строки, вы можете передать срезы с тремя компонентами (т. е. старт, стоп, шаг).
опять же, для вашего примера выше:
>>> 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]]
часть), а затем повторно нарезать это-создание третьего массива - включая только столбцы 1 и 3 предыдущего массива.
у меня есть аналогичный вопрос здесь:запись в суб-ndarray из ndarray самым пифонским способом. 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]])