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.

2
  • 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? Commented Sep 1, 2020 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 Commented Sep 1, 2020 at 23:15

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

Comments

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.

Comments

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]])

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.