3

I find a Incomprehensible phenomenon this afternoon.I want to exchange two lines in ndarray shown as following code.

import numpy as np

a = np.random.randint(0, 40, size=(4, 4))
a = a + a.T
b = a.copy()
print(a)
b[1], b[3] = b[3], b[1]
print(b)

But an unexpected result came.The printing result as follows

[[60 64 12 33]
 [64 30 29 60]
 [12 29 40 64]
 [33 60 64 76]]

[[60 64 12 33]
 [33 60 64 76]
 [12 29 40 64]
 [33 60 64 76]]

i know a,b = b,a is effective.i want to know what difference between them? Can u talk about this question in terms of memory? thank u in advance!

metatoaster
  • 17,419
  • 5
  • 55
  • 66
vincen
  • 33
  • 3
  • [Previously asked before but accepted answer did not do the right thing, though second answer does](https://stackoverflow.com/questions/22847410/swap-two-values-in-a-numpy-array). – metatoaster Mar 13 '21 at 10:15
  • @metatoaster It **does** do the right thing. It's just not applicable to 2D. The question there is explicitly about 1D. – Manuel Mar 13 '21 at 10:18
  • Does this answer your question? [Swap slices of Numpy arrays](https://stackoverflow.com/questions/14933577/swap-slices-of-numpy-arrays) – iGian Mar 13 '21 at 10:18
  • @Manuel Does not change the fact that commenters on the accepted answer note that the solution is not safe in numpy, and clearly does not give the expected result when naively applied by beginners. Traps like these are not useful for newbies. – metatoaster Mar 13 '21 at 10:21
  • The "surprising" thing is that the assignments are sequential from left to right, and not concurrent/atomic. – Jan Christoph Terasa Mar 13 '21 at 10:45

1 Answers1

10

b[3] and b[1] are views into the original array. They don't copy their data. This is what you want most of the time.

So when you write b[1], b[3] = b[3], b[1] first the third row gets assigned to the first row, changing the array b. At this point b[1] has been changed, so when b[3] = b[1] happens your original data is gone.

b[[1, 3]] = b[[3, 1]] does what you want (also known as 'fancy indicing', this doesn't create views).

orlp
  • 112,504
  • 36
  • 218
  • 315
  • The reason the "fancy indexing" works is that there is also just a single assignment, as opposed to _two_ in the tuple assignment case, which are handled sequentially. I don't think the argument of copy or view is the point here, really. – Jan Christoph Terasa Mar 13 '21 at 10:51
  • @JanChristophTerasa, `b[1], b[3] = b[3], b[1].copy()` gets it right. `b[3]` needs the original value of `b[1]`, not the 'current'. This kind of assignment is ok with nested lists. Though for a list, `b[1][:], b[3][:] = b[3], b[1]` has the same problem. In all cases the tuple assignments ('unpacking') are sequential. The difference lies in whether `b[1]` has changed in the mean time. – hpaulj Mar 13 '21 at 17:32