user2082745
user2082745

Reputation: 381

Swap slices of Numpy arrays

I love the way python is handling swaps of variables: a, b, = b, a

and I would like to use this functionality to swap values between arrays as well, not only one at a time, but a number of them (without using a temp variable). This does not what I expected (I hoped both entries along the third dimension would swap for both):

import numpy as np
a = np.random.randint(0, 10, (2, 3,3))
b = np.random.randint(0, 10, (2, 5,5))
# display before
a[:,0, 0]
b[:,0,0]
a[:,0,0], b[:, 0, 0] = b[:, 0, 0], a[:,0,0] #swap
# display after
a[:,0, 0]
b[:,0,0]

Does anyone have an idea? Of course I can always introduce an additional variable, but I was wondering whether there was a more elegant way of doing this.

Upvotes: 38

Views: 13393

Answers (4)

user4815162342
user4815162342

Reputation: 155046

Python correctly interprets the code as if you used additional variables, so the swapping code is equivalent to:

t1 = b[:,0,0]
t2 = a[:,0,0]
a[:,0,0] = t1
b[:,0,0] = t2

However, even this code doesn't swap values correctly! This is because Numpy slices don't eagerly copy data, they create views into existing data. Copies are performed only at the point when slices are assigned to, but when swapping, the copy without an intermediate buffer destroys your data. This is why you need not only an additional variable, but an additional numpy buffer, which general Python syntax can know nothing about. For example, this works as expected:

t = np.copy(a[:,0,0])
a[:,0,0] = b[:,0,0]
b[:,0,0] = t

Upvotes: 31

RAHUL TIWARI
RAHUL TIWARI

Reputation: 9

This will work.

a[:,0,0], b[:, 0, 0] = b[:, 0, 0].copy(), a[:, 0, 0].copy()

Upvotes: -1

Pietro Battiston
Pietro Battiston

Reputation: 8380

user4815162342's answer is indeed the "right" one. But if you're really after a one-liner, then consider this:

a[arange(2),0,0], b[arange(2), 0, 0] = b[arange(2), 0, 0], a[arange(2),0,0] #swap

This is however significantly less efficient:

In [12]: %timeit a[arange(2),0,0], b[arange(2), 0, 0] = b[arange(2), 0, 0], a[arange(2),0,0]
10000 loops, best of 3: 32.2 µs per loop

In [13]: %timeit t = np.copy(a[:,0,0]); a[:,0,0] = b[:,0,0]; b[:,0,0] = t
The slowest run took 4.14 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 13.3 µs per loop

(but notice the note about "the slowest run"... if you try to call %timeit with "-n 1 -r 1", you will see more comparable results - although with my solution still being ~50% slower - demonstrating that yes, caching is affecting the timings)

Upvotes: 1

blaz
blaz

Reputation: 4248

I find this the simplest:

a[:,0,0], b[:, 0, 0] = b[:, 0, 0], a[:, 0, 0].copy() #swap

Time comparison:

%timeit a[:,0,0], b[:, 0, 0] = b[:, 0, 0], a[:, 0, 0].copy() #swap
The slowest run took 10.79 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 1.75 µs per loop

%timeit t = np.copy(a[:,0,0]); a[:,0,0] = b[:,0,0]; b[:,0,0] = t
The slowest run took 10.88 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 2.68 µs per loop

Upvotes: 12

Related Questions