3

I have a NumPy array as follows:

arr = np.array([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20]])

I am looking to arrange such that it looks like this:

[[[6,7,8,9,10],
  [1,2,3,4,5]],
 [[11,12,13,14,15],
  [6,7,8,9,10]],
 [[16,17,18,19,20],
  [11,12,13,14,15]]]

So essentially a 3D array with 2x5 in each row of the array. The code I tried is:

x=np.zeros([3,2,5])
for i in range(len(arr)):
    x[i]=arr[i:i+2,:][::-1]

But this results in the below output:

[[[ 6.  7.  8.  9. 10.]
  [ 1.  2.  3.  4.  5.]]    
 [[ 0.  0.  0.  0.  0.]
  [ 0.  0.  0.  0.  0.]]  
 [[ 0.  0.  0.  0.  0.]
  [ 0.  0.  0.  0.  0.]]]

[[[ 6.  7.  8.  9. 10.]
  [ 1.  2.  3.  4.  5.]]    
 [[11. 12. 13. 14. 15.]
  [ 6.  7.  8.  9. 10.]]    
 [[ 0.  0.  0.  0.  0.]
  [ 0.  0.  0.  0.  0.]]]

[[[ 6.  7.  8.  9. 10.]
  [ 1.  2.  3.  4.  5.]]    
 [[11. 12. 13. 14. 15.]
  [ 6.  7.  8.  9. 10.]]    
 [[16. 17. 18. 19. 20.]
  [11. 12. 13. 14. 15.]]]
Georgy
  • 12,464
  • 7
  • 65
  • 73
FlyingPickle
  • 1,047
  • 1
  • 9
  • 19
  • When I try executing your code attempt I get an error, just as I'd expect. If I change the loop's `range` to `range(x.shape[0])` (i.e. `range(len(x))`) I get the result you want. Are you sure the array you pasted came from the above input and code? – Andras Deak -- Слава Україні Feb 02 '19 at 00:45

3 Answers3

1

You can use some stride tricks to construct your array as a multidimensional sliding window over your input array:

import numpy as np 
arr = np.array([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20]]) 

# compute the strides and shape of the output array
in_strides = arr.strides 
out_strides = in_strides[:1] + in_strides 
out_shape = (3, 2) + arr.shape[-1:]  # keep the last dimension's size
strided = np.lib.stride_tricks.as_strided(arr, strides=out_strides, shape=out_shape)
out_arr = strided[:, ::-1, :].copy()  # use a copy to stay safe

The above will work safely as long as out_shape[-1] <= arr.shape[1] and sum(out_shape[:2]) <= arr.shape[0] + 1. These are constraints that make the sliding window meaningful inside the original array, and your actual use case should naturally respect these.

Important notes:

  • If the above inequalities don't hold then the sliding window will happily slide out of the memory range of your array, and you'll silently start seeing garbage matrix elements:

    >>> out_strides = in_strides[:1] + in_strides 
    ... out_shape = (3, 3, 5)  # 3 + 3 == 6 > arr.shape[0] + 1 == 5
    ... np.lib.stride_tricks.as_strided(arr, strides=out_strides, shape=out_shape)
    array([[[         1,              2,              3,              4,
                      5],
        [             6,              7,              8,              9,
                     10],
        [            11,             12,             13,             14,
                     15]],
    
       [[             6,              7,              8,              9,
                     10],
        [            11,             12,             13,             14,
                     15],
        [            16,             17,             18,             19,
                     20]],
    
       [[            11,             12,             13,             14,
                     15],
        [            16,             17,             18,             19,
                     20],
        [           384,            193, 94379169559968,              0,
                      0]]])
    
  • If you won't mutate your array afterward, and only then, you may omit the final .copy() call in the above. This will give you a strided array that shares memory with the original array, but what's more important, the rows of the array will share memory with one another. This is not what you usually want, but if your real array is very large and you know you can safely assume that the values won't be independently mutated, the memory footprint might matter. Another aspect to consider is that calling .copy() on the result will give you a contiguous block of memory, which is probably better for performance down the line, depending on what you're planning to do with the resulting array.
1

We can leverage np.lib.stride_tricks.as_strided based scikit-image's view_as_windows to get sliding windows. More info on use of as_strided based view_as_windows.

from skimage.util.shape import view_as_windows

x = view_as_windows(arr,(2,arr.shape[1]))[:,0,::-1]

This would simply be a view into the input array. Hence, there's no extra memory overhead and virtually free runtime. If you want an output with it's own memory space, append with .copy() there, i.e. x.copy().

Sample run -

In [15]: from skimage.util.shape import view_as_windows

In [16]: view_as_windows(arr,(2,arr.shape[1]))[:,0,::-1]
Out[16]: 
array([[[ 6,  7,  8,  9, 10],
        [ 1,  2,  3,  4,  5]],

       [[11, 12, 13, 14, 15],
        [ 6,  7,  8,  9, 10]],

       [[16, 17, 18, 19, 20],
        [11, 12, 13, 14, 15]]])
Divakar
  • 218,885
  • 19
  • 262
  • 358
0

No need to use any loops. Slicing suffices:

x = np.zeros([3,2,5], dtype=int)
x[:,0] = arr[-3:,:]
x[:,1] = arr[:3,:]

Essentially you assign the 0-th row in all pages to the last 3 rows of arr, and the 1st row in all pages to the first 3 rows of arr.

Shuhao Cao
  • 221
  • 1
  • 7