2

How can I slice a numpy array beyond its shape such that values in the array are repeated without having to store the entire array in memory? Here's what I'd like to do:

x = numpy.array([[1, 2], [3, 4]])
x[0:3, 0:3]
->
[[1, 2, 1, 2],
 [3, 4, 3, 4],
 [1, 2, 1, 2],
 [3, 4, 3, 4]]

I am aware of numpy.repeat and numpy.tile but both of these make a copy of the array, and I would like to slice my array like x[1238123:1238143,5328932:5328941] without needing to make millions of copies of the smaller array.

Rich
  • 12,068
  • 9
  • 62
  • 94
  • 2
    Can you please explain why? I mean, if you know the array repeats and the period it repeats, why do you need to access it with `1238123:1238143,5328932:5328941` specifically? – DeepSpace Sep 01 '20 at 23:09
  • 1
    Anyway, maybe `x[(x.shape[0] - 1) % 1238123: (x.shape[0] - 1) % 1238143, (x.shape[1] - 1) % 5328932: (x.shape[1] - 1) % 5328941]` is what you are looking for? If so, this can be wrapped in a function or by subclassing `np.ndarray` – DeepSpace Sep 01 '20 at 23:15

3 Answers3

4

With strides tricks we can make an 4d view:

In [18]: x = numpy.array([[1, 2], [3, 4]])
In [19]: as_strided = np.lib.stride_tricks.as_strided
In [20]: X = as_strided(x, shape=(2,2,2,2), strides=(0,16,0,8))
In [21]: X
Out[21]: 
array([[[[1, 2],
         [1, 2]],

        [[3, 4],
         [3, 4]]],


       [[[1, 2],
         [1, 2]],

        [[3, 4],
         [3, 4]]]])

Which can be reshaped into your desired array:

In [22]: X.reshape(4,4)
Out[22]: 
array([[1, 2, 1, 2],
       [3, 4, 3, 4],
       [1, 2, 1, 2],
       [3, 4, 3, 4]])

But that reshaping will create a copy of X.

That (2,2) array can be used in calculations as (1,1,2,2) array, which if needed is expanded to (2,2,2,2):

In [25]: x[None,None,:,:]
Out[25]: 
array([[[[1, 2],
         [3, 4]]]])
In [26]: np.broadcast_to(x,(2,2,2,2))
Out[26]: 
array([[[[1, 2],
         [3, 4]],

        [[1, 2],
         [3, 4]]],


       [[[1, 2],
         [3, 4]],

        [[1, 2],
         [3, 4]]]])

Thus broadcasting lets us use a view of an array in larger calculations.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
2

NumPy arrays don't support that. An array has to have a consistent stride in each dimension, and the array you want wouldn't have that.

You could implement your own custom type for the result, but it wouldn't work at NumPy speed, and it wouldn't be directly compatible with NumPy - at best, any NumPy function you tried to call would have to build a real array out of your object first.

If your use case only needs small slices, as with your x[1238123:1238143,5328932:5328941] example, your best bet is probably to adjust the slice endpoints down to equivalent, smaller values, then tile and slice.

user2357112
  • 260,549
  • 28
  • 431
  • 505
0

Use numpy.ndarray.take twice for 2D array (triple for 3D array, etc.). Each time you'd specify a different axis. For the case you required:

x.take(range(0, 4), mode='wrap', axis = 0).take(range(0, 4), mode='wrap', axis = 1)

which would produce

array([[1, 2, 1, 2],
       [3, 4, 3, 4],
       [1, 2, 1, 2],
       [3, 4, 3, 4]])
Raymond
  • 41
  • 4