Is there an `np.repeat` which acts on an existing array?

Viewed 88

I have a large NumPy array which I want to fill with new data on each iteration of a loop. The array is filled with data repeated along axis 0, for example:

[[1, 5],
 [1, 5],
 [1, 5],
 [1, 5]]

I know how to create this array from scratch in each iteration:

x = np.repeat([[1, 5]], 4, axis=0)

However, I don't want to create a new array every time, because it's a very large array (much larger than 4x2). Instead, I want to create the array in advance using the above code, and then just fill the array with new data on each iteration.

But np.repeat() returns a new array, rather than acting on an existing array. Is there an equivalent of np.repeat() for filling an existing array?

1 Answers

As we noted in comments, you can use a broadcasting assignment to fill your 2d array with a 1d array-like of the appropriate size:

x[...] = [1, 5]

If by any chance your large array always contains the same items in each row (i.e. you won't change these preset values later), you can almost certainly use broadcasting in later parts of your code and just work with an initial x such as

x = np.array([[1, 5]])

This array has shape (1, 2) which is broadcast-compatible with other arrays of shape (4, 2) you might have in the above example.

If you always need the same values in each row and for some reason you can't use broadcasting (both cases are highly unlikely), you can use broadcast_to to create an array with an explicit 2d shape without copying memory:

x_bc = np.broadcast_to([1, 5], (4, 2)) # broadcast 1d [1, 5] to shape (4, 2)

This might work because it has the right shape with only 2 unique elements in memory:

>>> x_bc
array([[1, 5],
       [1, 5],
       [1, 5],
       [1, 5]])

>>> x_bc.strides
(0, 8)

However you can't mutate it, because it's a read-only view:

>>> x_bc[0, :] = [2, 4]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-35-ae12ecfe3c5e> in <module>
----> 1 x_bc[0, :] = [2, 4]

ValueError: assignment destination is read-only

So, if you only need the same values in each row and you can't use broadcasting and you want to mutate those same rows later, you can use stride tricks to map the same 1d data to a 2d array:

>>> x_in = np.array([1, 5])
... x_strided = np.lib.stride_tricks.as_strided(x_in, shape=(4,) + x_in.shape,
...                                             strides=(0,) + x_in.strides[-1:])

>>> x_strided
array([[1, 5],
       [1, 5],
       [1, 5],
       [1, 5]])

>>> x_strided[0, :] = [2, 4]

>>> x_strided
array([[2, 4],
       [2, 4],
       [2, 4],
       [2, 4]])

Which gives you a 2d array of fixed shape that always contains one unique row, and mutating any of the rows mutates the rest (since the underlying data corresponds to only a single row). Handle with care, because if you ever want to have two different rows you'll have to do something else.

Related