Is it possible to shuffle a numpy array in place by a defined index list?

Viewed 331

Re-ordering a numpy array with a given list of indexes, like the following:

arr = np.array([10, 20, 30, 40, 50])
idx = [1, 0, 3, 4, 2]
arr[idx]

will result in making a copy of the whole array.

However, the numpy.shuffle() method works in place:

np.random.shuffle(arr)

will not make a copy of the array and shuffle it IN PLACE.

So it seems like there should be a way of shuffling an array in place, but with a pre-ordered list of indexes? How can this be done?

1 Answers

At the risk of duplicating my previous answer, I'll try to illustrate the problem with doing an in-place shuffle without any sort of copy.

Your sample:

In [196]: arr = np.array([10, 20, 30, 40, 50])
     ...: idx = [1, 0, 3, 4, 2]

The straightforward approach:

In [197]: arr[idx]
Out[197]: array([20, 10, 40, 50, 30])

Effectively it is doing:

In [198]: [arr[1],arr[0],arr[3],arr[4],arr[2]]
Out[198]: [20, 10, 40, 50, 30]

A naive iterative approach:

In [199]: for i,v in enumerate(idx):
     ...:     arr[i] = arr[v]
     ...: 
In [200]: arr
Out[200]: array([20, 20, 40, 50, 40])

Oops - that has stepped on values. arr[1] has been copied to [0]. That is then copied [1]. The 10 and 30 have been lost.

The iterative solution requires a copy:

In [201]: arr = np.array([10, 20, 30, 40, 50])
     ...: idx = [1, 0, 3, 4, 2]
In [202]: arr1 = arr.copy()
     ...: for i,v in enumerate(idx):
     ...:     arr[i] = arr1[v]
     ...: 
In [203]: arr
Out[203]: array([20, 10, 40, 50, 30])

In [205]: arr[idx]=arr In [206]: arr Out[206]: array([10, 10, 30, 30, 30])

It may be possible to come up with an indexing that avoids this, but it would take some thought. I suspect old classical computer science (Knuth?) worked this out, since old computers has much smaller RAM, and often depended on values read serially from tape.

Other indexing attempts:

In [205]: arr[idx]=arr
In [206]: arr
Out[206]: array([10, 10, 30, 30, 30])
In [207]: arr = np.array([10, 20, 30, 40, 50])
     ...: idx = [1, 0, 3, 4, 2]
In [208]: arr[idx]=arr.copy()
In [209]: arr
Out[209]: array([20, 10, 50, 30, 40])
Related