使用 numpy 构建一个数组,其中的行从另一个二维数组中提取为 2x2 块
Using numpy to construct an array with rows extracted from another 2D array as 2x2 blocks
假设我有以下二维数组:
x = np.array([[10,20,30,40], [50,60,70,80],[90,100,110,120]])
print(x)
array([[ 10, 20, 30, 40],
[ 50, 60, 70, 80],
[ 90, 100, 110, 120]])
我想构建一个新数组,y
,其中每一行都有来自 x
的 2x2 块的值,按顺时针顺序:
print(y)
array([[ 10, 20, 60, 50],
[ 20, 30, 70, 60],
[ 30, 40, 80, 70],
[ 50, 60, 100, 90],
[ 60, 70, 110, 100],
[ 70, 80, 120, 110]])
我可以使用 Python for 循环实现,如下所示:
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
y = np.array(y)
我想知道是否有更快的方法利用 Numpy 函数并避免使用 Python 循环。
你可以缓存你的代码,因为循环主要是一次又一次地迭代同一个矩阵(如果你想在循环中保留相同的代码)。我已经为你的代码做了缓存前后的速度对比。
# Before caching
def loop_before_cache():
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
return np.array(y)
%timeit loop_before_cache()
11.6 µs ± 318 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
现在有了缓存
# After caching
from functools import lru_cache
@lru_cache()
def loop_after_cache():
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
return np.array(y)
%timeit loop_after_cache()
83.6 ns ± 2.42 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
额外
我使用 range
添加了带有 (1000,5000) 数组的模拟数据,以显示缓存的效率。
x = np.array([i for i in range(1,5000001)])
x = np.reshape(x, (1000,5000))
# Before caching
def loop_before_cache():
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
return np.array(y)
%timeit loop_before_cache()
8.58 s ± 113 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# After caching
@lru_cache(maxsize = 256)
def loop_after_cache():
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
return np.array(y)
%timeit loop_after_cache()
82.2 ns ± 5.58 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
首先,在 x
中创建一个 sliding_window_view
,其中包含您要查看的 2x2 框:
b = np.lib.stride_tricks.sliding_window_view(x, (2, 2))
最里面的每个 2x2 数组都包含您想要的未拆解版本,但数组的第二部分颠倒了。到目前为止,我们没有复制任何数据。现在通过拼凑最后一个维度来制作副本。由于 b
高度 non-contiguous:
,因此 reshape 将始终在此处制作副本
c = b.reshape(*b.shape[:2], 4)
交换最后两列:
c[..., 2:] = c[..., -1:1:-1]
现在阐明主要维度:
y = c.reshape(-1, c.shape[-1])
如果您的 numpy 版本低于 1.20,您可以将 b
的定义替换为
b = np.lib.stride_tricks.as_strided(x, shape=(x.shape[0] - 1, x.shape[1] - 1, 2, 2), strides=x.strides * 2)
假设我有以下二维数组:
x = np.array([[10,20,30,40], [50,60,70,80],[90,100,110,120]])
print(x)
array([[ 10, 20, 30, 40],
[ 50, 60, 70, 80],
[ 90, 100, 110, 120]])
我想构建一个新数组,y
,其中每一行都有来自 x
的 2x2 块的值,按顺时针顺序:
print(y)
array([[ 10, 20, 60, 50],
[ 20, 30, 70, 60],
[ 30, 40, 80, 70],
[ 50, 60, 100, 90],
[ 60, 70, 110, 100],
[ 70, 80, 120, 110]])
我可以使用 Python for 循环实现,如下所示:
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
y = np.array(y)
我想知道是否有更快的方法利用 Numpy 函数并避免使用 Python 循环。
你可以缓存你的代码,因为循环主要是一次又一次地迭代同一个矩阵(如果你想在循环中保留相同的代码)。我已经为你的代码做了缓存前后的速度对比。
# Before caching
def loop_before_cache():
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
return np.array(y)
%timeit loop_before_cache()
11.6 µs ± 318 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
现在有了缓存
# After caching
from functools import lru_cache
@lru_cache()
def loop_after_cache():
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
return np.array(y)
%timeit loop_after_cache()
83.6 ns ± 2.42 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
额外
我使用 range
添加了带有 (1000,5000) 数组的模拟数据,以显示缓存的效率。
x = np.array([i for i in range(1,5000001)])
x = np.reshape(x, (1000,5000))
# Before caching
def loop_before_cache():
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
return np.array(y)
%timeit loop_before_cache()
8.58 s ± 113 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# After caching
@lru_cache(maxsize = 256)
def loop_after_cache():
n_rows, n_cols = x.shape
y = []
for i in range(n_rows-1):
for j in range(n_cols-1):
row = [x[i,j],x[i,j+1],x[i+1, j+1],x[i+1,j]]
y.append(row)
return np.array(y)
%timeit loop_after_cache()
82.2 ns ± 5.58 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
首先,在 x
中创建一个 sliding_window_view
,其中包含您要查看的 2x2 框:
b = np.lib.stride_tricks.sliding_window_view(x, (2, 2))
最里面的每个 2x2 数组都包含您想要的未拆解版本,但数组的第二部分颠倒了。到目前为止,我们没有复制任何数据。现在通过拼凑最后一个维度来制作副本。由于 b
高度 non-contiguous:
c = b.reshape(*b.shape[:2], 4)
交换最后两列:
c[..., 2:] = c[..., -1:1:-1]
现在阐明主要维度:
y = c.reshape(-1, c.shape[-1])
如果您的 numpy 版本低于 1.20,您可以将 b
的定义替换为
b = np.lib.stride_tricks.as_strided(x, shape=(x.shape[0] - 1, x.shape[1] - 1, 2, 2), strides=x.strides * 2)