Numpy中的分块操作

11

有没有方便的实用工具可以在NumPy数组上进行分块操作?

我想的是像玻尔兹曼自旋重整化这样的操作,其中你将矩阵分成块,并返回一个矩阵,其中每个块被其总和、平均值或其他函数所替代。

3个回答

8
你可能正在寻找superbatfish的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]])

如果您希望将其“切成”形状为(3,2)的4个块,则可以使用{{link1: blockwise_view }}将其转换为形状为(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])

我的第一个答案不同,它只能应用于2D数组,blockwise_view可以应用于任意N维数组。它返回一个2N维数组,其中前N个轴索引块。

Scikit-image 中还有 view_as_windows,Scikit-learn 中有 extract_patches - user2379410

2

对于滑动分块操作,您可以借鉴在Python中实现Matlab的im2col_sliding“sliding” 的实现方法,将每个块分组到一列中,因此分块操作就像沿着axis=0进行操作一样容易,并且能够接受所有NumPy ufuncs以进行向量化解决方案。这里是定义这样一个滑动分块创建函数的正式方式 -

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())

计算块状的 sum, average, std 等的示例运行 -

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])

0

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

例如,对于一个形状为(6, 12, 20)的数组,并针对块大小为(2, 3, 4),重塑为(3, 2, 4, 3, 5, 4)即可。

重塑后,块位置和块大小轴是交替的:

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

但它们可以很容易地通过np.transpose()重新排列。

以下是一个二维示例:

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]]]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接