有没有方便的实用工具可以在NumPy数组上进行分块操作?
我想的是像玻尔兹曼自旋重整化这样的操作,其中你将矩阵分成块,并返回一个矩阵,其中每个块被其总和、平均值或其他函数所替代。
blockwise_view
。这个函数使用np.lib.stride_tricks.as_strided
创建一个数组视图,将数组的“块”放置在它们自己的轴上。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个轴索引块。
对于滑动分块操作,您可以借鉴在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])
这也可以通过简单的重塑来完成。 思路是交错每个维度的块数和块大小。
例如,对于一个形状为(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]]]]
view_as_windows
,Scikit-learn 中有extract_patches
。 - user2379410