Numpy中的Blockwise操作

时间:2015-12-17 01:28:36

标签: arrays numpy matrix

是否有任何便利工具在Numpy阵列上进行块运算?

我正在考虑像Ising自旋重整化这样的操作,其中将矩阵划分为块并返回矩阵,其中每个块被其求和,平均值或其他函数替换。

3 个答案:

答案 0 :(得分:6)

您可能正在寻找superbatfish's blockwise_view。这使用np.lib.stride_tricks.as_strided来创建一个数组视图,该视图将数组的“块”放在它们自己的轴中。

例如,假设您有一个2D数组,例如

In [97]: arr = np.arange(24).reshape(6, 4)

In [98]: arr.shape
Out[98]: (6, 4)

In [99]: arr
Out[99]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])

并且您希望将其“切成”为4块形状(3,2)。你可以用 blockwise_view将其转换为形状为4D的数组(4,3,2):

In [34]: blocked = blockwise_view(arr, (3, 2)); blocked
Out[34]: 
array([[[[ 0,  1],
         [ 4,  5],
         [ 8,  9]],

        [[ 2,  3],
         [ 6,  7],
         [10, 11]]],


       [[[12, 13],
         [16, 17],
         [20, 21]],

        [[14, 15],
         [18, 19],
         [22, 23]]]])

In [37]: blocked.shape
Out[37]: (2, 2, 3, 2)

现在您可以对其进行整形,以便一个块中的所有值都在最后一个轴中:

In [41]: reshaped = blocked.reshape(-1, 3*2); reshaped
Out[41]: 
array([[ 0,  1,  4,  5,  8,  9],
       [ 2,  3,  6,  7, 10, 11],
       [12, 13, 16, 17, 20, 21],
       [14, 15, 18, 19, 22, 23]])

现在您可以沿着该轴求和,或者取其平均值或将其他函数应用于每个块的元素:

In [103]: reshaped.sum(axis=-1)
Out[103]: array([ 27,  39,  99, 111])

In [104]: reshaped.mean(axis=-1)
Out[104]: array([  4.5,   6.5,  16.5,  18.5])

my first answer不同,{{3}}只能应用于2D数组, blockwise_view可以应用于任意N维数组。它返回一个 2N维数组,其中前N个轴索引块。

答案 1 :(得分:2)

对于滑动块运算,您可以从Implement Matlab's im2col_sliding 'sliding' in python借用一个实现,该实现将每个块分组为一列,从而逐块操作将变得像在axis = 0上操作一样简单,因此会接受所有{ {3}}用于矢量化解决方案。这是定义这种滑块创建函数的正式方法 -

def im2col_sliding(A,BLKSZ):   

    # Parameters
    M,N = A.shape
    col_extent = N - BLKSZ[1] + 1
    row_extent = M - BLKSZ[0] + 1

    # Get Starting block indices
    start_idx = np.arange(BLKSZ[0])[:,None]*N + np.arange(BLKSZ[1])

    # Get offsetted indices across the height and width of input array
    offset_idx = np.arange(row_extent)[:,None]*N + np.arange(col_extent)

    # Get all actual indices & index into input array for final output
    return np.take (A,start_idx.ravel()[:,None] + offset_idx.ravel())

运行示例以计算块sumaveragestd等 -

In [6]: arr                 # Sample array
Out[6]: 
array([[6, 5, 0, 6, 0],
       [7, 4, 2, 3, 6],
       [6, 3, 3, 8, 1],
       [5, 5, 1, 1, 8]])

In [7]: im2col_sliding(arr,[2,3])   # Blockwise array with blocksize : (2,3)
Out[7]: 
array([[6, 5, 0, 7, 4, 2, 6, 3, 3],
       [5, 0, 6, 4, 2, 3, 3, 3, 8],
       [0, 6, 0, 2, 3, 6, 3, 8, 1],
       [7, 4, 2, 6, 3, 3, 5, 5, 1],
       [4, 2, 3, 3, 3, 8, 5, 1, 1],
       [2, 3, 6, 3, 8, 1, 1, 1, 8]])

In [8]: np.sum(im2col_sliding(arr,[2,3]),axis=0) # Perform blockwise summation
Out[8]: array([24, 20, 17, 25, 23, 23, 23, 21, 22])

In [9]: np.mean(im2col_sliding(arr,[2,3]),axis=0) # Perform blockwise averaging
Out[9]: 
array([ 4.        ,  3.33333333,  2.83333333,  4.16666667,  3.83333333,
        3.83333333,  3.83333333,  3.5       ,  3.66666667])

In [10]: np.std(im2col_sliding(arr,[2,3]),axis=0) # Blockwise std. deviation
Out[10]: 
array([ 2.38047614,  1.97202659,  2.47767812,  1.77169097,  1.95078332,
        2.40947205,  1.67497927,  2.43241992,  3.14466038])

答案 2 :(得分:0)

这也可以通过简单的重塑来完成。 想法是交错每个维度的块数和块大小。

例如,对于形状为(6, 12, 20)的数组,并且目标块大小为(2, 3, 4),则可以重塑为(3, 2, 4, 3, 5, 4)

整形后,块位置和块大小轴交替:

  • 偶数索引指的是块重复
  • 奇数索引指的是块大小

,但是可以很容易地用np.transpose()重新排列它们。

一个2D示例如下:

import numpy as np

block_shape = 2, 2
repeats = 3, 3

m = repeats[0] * block_shape[0]
n = repeats[1] * block_shape[1]

arr = np.arange((m * n)).reshape((m, n))
print(arr)
# [[ 0  1  2  3  4  5]
#  [ 6  7  8  9 10 11]
#  [12 13 14 15 16 17]
#  [18 19 20 21 22 23]
#  [24 25 26 27 28 29]
#  [30 31 32 33 34 35]]

block_shape = tuple(x for nm in zip(repeats, block_shape) for x in nm)
# (3, 2, 3, 2)
repeat_indexes = tuple(range(0, len(block_shape), 2))
# 0, 2
block_indexes = tuple(range(1, len(block_shape), 2))
# 1, 3
block_arr = arr.reshape(block_shape).transpose(repeat_indexes + block_indexes)
print(block_arr)
# [[[[ 0  1]
#    [ 6  7]]
#   [[ 2  3]
#    [ 8  9]]
#   [[ 4  5]
#    [10 11]]]
#  [[[12 13]
#    [18 19]]
#   [[14 15]
#    [20 21]]
#   [[16 17]
#    [22 23]]]
#  [[[24 25]
#    [30 31]]
#   [[26 27]
#    [32 33]]
#   [[28 29]
#    [34 35]]]]