Suppose I have the following 2D array:
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]])
I would like to construct a new array, y, that contains the following:
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]])
I could achieve that using Python for loops as follows:
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)
I wonder if there is a faster way that takes advantage of Numpy functions and avoid using Python loops.
Thank you
>Solution :
First, create a sliding_window_view into x with the 2×2 boxes you want to see:
b = np.lib.stride_tricks.sliding_window_view(x, (2, 2))
Each of the innermost 2×2 arrays contains an unraveled version of what you want, but with the second part of the array reversed. So far we didn’t copy any data. Now make a copy by raveling the last dimension. The reshape will always make a copy here because b is highly non-contiguous:
c = b.reshape(*b.shape[:2], 4)
Swap the last two columns:
c[..., 2:] = c[..., -1:1:-1]
Now ravel the leading dimensions:
y = c.reshape(-1, c.shape[-1])
If you have a version of numpy that is older whan 1.20, you can replace the definition of b with
b = np.lib.stride_tricks.as_strided(x, shape=(x.shape[0] - 1, x.shape[1] - 1, 2, 2), strides=x.strides * 2)