是否有任何便利工具在Numpy阵列上进行块运算?
我正在考虑像Ising自旋重整化这样的操作,其中将矩阵划分为块并返回矩阵,其中每个块被其求和,平均值或其他函数替换。
答案 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())
运行示例以计算块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])
答案 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]]]]