Извлечение блоков или патчей из массива NumPy

У меня есть двумерный массив numpy следующим образом:

a = np.array([[1,5,9,13],
              [2,6,10,14],
              [3,7,11,15],
              [4,8,12,16]]

Я хочу извлечь его в патчи размером 2 на 2 без повторения элементов.

Ответ должен быть точно таким же. Это может быть трехмерный массив или список с таким же порядком элементов, как показано ниже:

[[[1,5],
 [2,6]],   

 [[3,7],
 [4,8]],

 [[9,13],
 [10,14]],

 [[11,15],
 [12,16]]]

Как это сделать легко?

В моей реальной задаче размер a равен (36, 72). Я не могу сделать это по одному. Я хочу программный способ сделать это.


person Borys    schedule 20.07.2015    source источник
comment
Я обновил свой ответ в заголовке stackoverflow.com/questions/26871083/. Учитывая этот вопрос и stackoverflow.com/questions/31494190/, я думаю, мы можем закрыть это как обман.   -  person Warren Weckesser    schedule 21.07.2015
comment
@WarrenWeckesser Можете ли вы показать мне ЗДЕСЬ, как вы будете извлекать патчи, которые я извлекал вручную в своем вопросе?   -  person Borys    schedule 21.07.2015
comment
@WarrenWeckesser Речь идет не о расчете среднего, как в вашем ответе.   -  person Borys    schedule 21.07.2015
comment
Я уже улучшил свое объяснение того, как ответ на stackoverflow.com/questions/26871083/ работает. Вы видели ту часть, которая начинается Обобщать...? Есть два шага: преобразовать массив в четырехмерный массив, а затем усреднить. Часть изменения формы такая же, как вы просите, поэтому я бы не хотел дублировать это здесь.   -  person Warren Weckesser    schedule 21.07.2015
comment
@WarrenWeckesser Я думаю, что вашего ответа более чем достаточно, чтобы он обобщил ответ (вы дали точную формулу, лол). Это определенно позорит мою маленькую попытку кодера на Python. Я рад, что посмотрел его.   -  person Trekkie    schedule 21.07.2015
comment
@WarrenWeckesser да, я прочитал ваш ответ, но он не показывает отдельные патчи, как я просил здесь. Я должен извлечь отдельные патчи. надеюсь ты мне поможешь   -  person Borys    schedule 21.07.2015
comment
В этом ответе y — это массив патчей. Чтобы получить доступ к патчу, используйте y[j, :, k, :]. В вашем случае исходного массива с формой (36, 72) j будет варьироваться от 0 до 17, а k будет варьироваться от 0 до 35.   -  person Warren Weckesser    schedule 21.07.2015
comment
@WarrenWeckesser Я имею в виду, что хочу, чтобы исправления были субметриками одного массива в качестве ответа на этот вопрос.   -  person Borys    schedule 21.07.2015
comment
А, значит, если вы вводите a, вы можете просто использовать a[2*j:2*j+2, 2*k:2*k+2]. Это то, что вы имели ввиду?   -  person Warren Weckesser    schedule 21.07.2015
comment
@WarrenWeckesser Можете ли вы поместить свой массив 4d с точным порядком элементов в мои патчи.   -  person Borys    schedule 21.07.2015
comment
@WarrenWeckesser j и k должны проходить цикл for?   -  person Borys    schedule 21.07.2015
comment
На мой взгляд, ваша коллекция патчей 2x2 по своей сути является двумерной коллекцией; y в другом ответе можно интерпретировать как двумерный массив двумерных патчей. Если это не сработает для вас, не могли бы вы изменить вопрос, чтобы более четко указать желаемую структуру данных, которую вы хотите получить в конце? Может быть, список 2-мерных массивов Python? Что-то другое? Что вы собираетесь делать с этой коллекцией патчей? (Желательно, чтобы вы отредактировали вопрос, чтобы уточнить, а не объяснять его здесь, в комментариях.)   -  person Warren Weckesser    schedule 21.07.2015
comment
@WarrenWeckesser да, я отредактировал свой вопрос, описывающий структуру и порядок элементов.   -  person Borys    schedule 21.07.2015
comment
@TrekkieByDay да, это единственный вопрос, оставшийся для получения степени MBA, лол!   -  person Borys    schedule 21.07.2015
comment
Удалил мой ответ, так как @WarrenWeckesser предоставил лучший.   -  person Trekkie    schedule 21.07.2015


Ответы (3)


Вот довольно загадочный однострочный код для создания трехмерного массива, который здесь называется result1:

In [60]: x
Out[60]: 
array([[2, 1, 2, 2, 0, 2, 2, 1, 3, 2],
       [3, 1, 2, 1, 0, 1, 2, 3, 1, 0],
       [2, 0, 3, 1, 3, 2, 1, 0, 0, 0],
       [0, 1, 3, 3, 2, 0, 3, 2, 0, 3],
       [0, 1, 0, 3, 1, 3, 0, 0, 0, 2],
       [1, 1, 2, 2, 3, 2, 1, 0, 0, 3],
       [2, 1, 0, 3, 2, 2, 2, 2, 1, 2],
       [0, 3, 3, 3, 1, 0, 2, 0, 2, 1]])

In [61]: result1 = x.reshape(x.shape[0]//2, 2, x.shape[1]//2, 2).swapaxes(1, 2).reshape(-1, 2, 2)

result1 подобен одномерному массиву двумерных массивов:

In [68]: result1.shape
Out[68]: (20, 2, 2)

In [69]: result1[0]
Out[69]: 
array([[2, 1],
       [3, 1]])

In [70]: result1[1]
Out[70]: 
array([[2, 2],
       [2, 1]])

In [71]: result1[5]
Out[71]: 
array([[2, 0],
       [0, 1]])

In [72]: result1[-1]
Out[72]: 
array([[1, 2],
       [2, 1]])

(Извините, сейчас у меня нет времени подробно рассказывать, как это работает. Может быть, позже...)

Вот менее загадочная версия, в которой используется понимание вложенного списка. В этом случае result2 представляет собой список python из двумерных массивов numpy:

In [73]: result2 = [x[2*j:2*j+2, 2*k:2*k+2] for j in range(x.shape[0]//2) for k in range(x.shape[1]//2)]

In [74]: result2[5]
Out[74]: 
array([[2, 0],
       [0, 1]])

In [75]: result2[-1]
Out[75]: 
array([[1, 2],
       [2, 1]])
person Warren Weckesser    schedule 21.07.2015
comment
Спасибо это то что я искал! - person Borys; 21.07.2015
comment
@Warren, ты заслуживаешь пива - person Eliethesaiyan; 15.05.2019
comment
Как бы вы отменили эту операцию? - person Fabian Fritz; 01.10.2020

Использование scikit-изображения:

import numpy as np
from skimage.util import view_as_blocks

a = np.array([[1,5,9,13],
              [2,6,10,14],
              [3,7,11,15],
              [4,8,12,16]])

print(view_as_blocks(a, (2, 2)))
person Stefan van der Walt    schedule 22.07.2015

Вы можете добиться этого с помощью комбинации np.reshape и < href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.swapaxes.html" rel="noreferrer">np.swapaxes вот так -

def extract_blocks(a, blocksize, keep_as_view=False):
    M,N = a.shape
    b0, b1 = blocksize
    if keep_as_view==0:
        return a.reshape(M//b0,b0,N//b1,b1).swapaxes(1,2).reshape(-1,b0,b1)
    else:
        return a.reshape(M//b0,b0,N//b1,b1).swapaxes(1,2)

Как видно, есть два способа его использования - с выключенным флагом keep_as_view (по умолчанию) или включенным. С keep_as_view = False мы изменяем форму переставленных осей до конечного результата 3D, а с keep_as_view = True мы сохраняем его 4D, и это будет представление во входной массив и, следовательно, практически бесплатно во время выполнения. Позже мы проверим это на тестовом примере.

Примеры случаев

Давайте используем образец входного массива, например:

In [94]: a
Out[94]: 
array([[2, 2, 6, 1, 3, 6],
       [1, 0, 1, 0, 0, 3],
       [4, 0, 0, 4, 1, 7],
       [3, 2, 4, 7, 2, 4],
       [8, 0, 7, 3, 4, 6],
       [1, 5, 6, 2, 1, 8]])

Теперь давайте используем некоторые размеры блоков для тестирования. Давайте использовать размер блока (2,3) с выключенным и включенным флагом просмотра -

In [95]: extract_blocks(a, (2,3)) # Blocksize : (2,3)
Out[95]: 
array([[[2, 2, 6],
        [1, 0, 1]],

       [[1, 3, 6],
        [0, 0, 3]],

       [[4, 0, 0],
        [3, 2, 4]],

       [[4, 1, 7],
        [7, 2, 4]],

       [[8, 0, 7],
        [1, 5, 6]],

       [[3, 4, 6],
        [2, 1, 8]]])

In [48]: extract_blocks(a, (2,3), keep_as_view=True)
Out[48]: 
array([[[[2, 2, 6],
         [1, 0, 1]],

        [[1, 3, 6],
         [0, 0, 3]]],


       [[[4, 0, 0],
         [3, 2, 4]],

        [[4, 1, 7],
         [7, 2, 4]]],


       [[[8, 0, 7],
         [1, 5, 6]],

        [[3, 4, 6],
         [2, 1, 8]]]])

Подтвердить view с помощью keep_as_view=True

In [20]: np.shares_memory(a, extract_blocks(a, (2,3), keep_as_view=True))
Out[20]: True

Давайте проверим производительность на большом массиве и проверим утверждение о практически бесплатном времени выполнения, как обсуждалось ранее.

In [42]: a = np.random.rand(2000,3000)

In [43]: %timeit extract_blocks(a, (2,3), keep_as_view=True)
1000000 loops, best of 3: 801 ns per loop

In [44]: %timeit extract_blocks(a, (2,3), keep_as_view=False)
10 loops, best of 3: 29.1 ms per loop
person Divakar    schedule 21.07.2015