Индексы подматриц фиксированного размера массива numpy
Я реализую алгоритм, который требует, чтобы я смотрел на непересекающиеся последовательные подматрицы в (строго двумерном) массиве numpy. например, для 12 на 12
>>> a = np.random.randint(20, size=(12, 12)); a
array([[ 4, 0, 12, 14, 3, 8, 14, 12, 11, 18, 6, 6],
[15, 13, 2, 18, 15, 15, 16, 2, 9, 16, 6, 4],
[18, 18, 3, 8, 1, 15, 14, 13, 13, 13, 7, 0],
[ 1, 9, 3, 6, 0, 4, 3, 15, 0, 9, 11, 12],
[ 5, 15, 5, 6, 4, 4, 18, 13, 10, 17, 11, 8],
[13, 17, 8, 15, 17, 12, 7, 1, 13, 15, 0, 18],
[ 2, 1, 11, 12, 3, 16, 11, 9, 10, 15, 4, 16],
[19, 11, 10, 7, 10, 19, 7, 13, 11, 9, 17, 8],
[14, 14, 17, 0, 0, 0, 11, 1, 10, 14, 2, 7],
[ 6, 15, 6, 7, 15, 19, 2, 4, 6, 16, 0, 3],
[ 5, 10, 7, 5, 0, 8, 5, 8, 9, 14, 4, 3],
[17, 2, 0, 3, 15, 10, 14, 1, 0, 7, 16, 2]])
и, глядя на подматрицы 3x3, я бы хотел, чтобы первая подматрица 3x3 была из верхнего левого угла:
>>> a[0:3, 0:3]
array([[ 4, 0, 12],
[15, 13, 2],
[18, 18, 3]])
Следующий вместе будет дан a[0:3, 3:6]
и так далее. Неважно, если последний такой набор индексов в каждой строке или столбце выходит за пределы конца массива - достаточно просто использовать numpy для того, чтобы просто передать часть в существующем срезе.
Мне нужен способ генерировать эти индексы срезов программно для матриц и подматриц произвольного размера. В настоящее время у меня есть это:
size = 3
x_max = a.shape[0]
xcoords = range(0, x_max, size)
xcoords = zip(xcoords, xcoords[1:])
и аналогично генерировать y_coords
, так что ряд индексов дается itertools.product(xcoords, ycoords)
,
Мой вопрос: есть ли более прямой способ сделать это, возможно, используя numpy.mgrid
или какая-то другая техника NumPy?
3 ответа
Получение индексов
Вот быстрый способ получить конкретный size x size
блок:
base = np.arange(size) # Just the base set of indexes
row = 1 # Which block you want
col = 0
block = a[base[:, np.newaxis] + row * size, base + col * size]
Если бы вы хотели, вы могли бы создать матрицы, аналогичные вашим xcoords
лайк:
y, x = np.mgrid[0:a.shape[0]/size, 0:a.shape[1]/size]
y_coords = y[..., np.newaxis] * size + base
x_coords = x[..., np.newaxis] * size + base
Тогда вы можете получить доступ к блоку, как это:
block = a[y_coords[row, col][:, np.newaxis], x_coords[row, col]]
Получение блоков напрямую
Если вы просто хотите получить блоки (а не индексы записей блоков), я бы использовал np.split
(дважды):
blocks = map(lambda x : np.split(x, a.shape[1]/size, 1), # Split the columns
np.split(a, a.shape[0]/size, 0)) # Split the rows
тогда у вас есть 2D список size x size
блоки:
>>> blocks[0][0]
array([[ 4, 0, 12],
[15, 13, 2],
[18, 18, 3]])
>>> blocks[1][0]
array([[ 1, 9, 3],
[ 5, 15, 5],
[13, 17, 8]])
Затем вы можете сделать этот массив массивом и использовать тот же стиль индексации, что и выше:
>>> blocks = np.array(blocks)
>>> blocks.shape
(4, 4, 3, 3)
Вы можете использовать однострочник:
r = 3
c = 3
lenr = a.shape[0]/r
lenc = a.shape[1]/c
np.array([a[i*r:(i+1)*r,j*c:(j+1)*c] for (i,j) in np.ndindex(lenr,lenc)]).reshape(lenr,lenc,r,c)
Я добавляю этот ответ к старому вопросу, так как редактирование подняло этот вопрос. Вот альтернативный способ вычисления блоков:
size = 3
lenr, lenc = int(a.shape[0]/size), int(a.shape[1]/size)
t = a.reshape(lenr,size,lenc,size).transpose(0, 2, 1, 3)
Профилирование показывает, что это самое быстрое. Профилирование выполняется с помощью Python 3.5, а результаты из map передаются в array() для совместимости, поскольку в 3.5 map возвращает итератор.
reshape/transpose: 643 ns per loop
reshape/index: 45.8 µs per loop
Map/split: 10.3 µs per loop
Интересно, что итераторная версия карты быстрее. В любом случае, использование reshape и transpose является самым быстрым.