unexpected result in numpy array slicing (view vs copy)

Question:

I’m trying to reduce the amount of copying in my code and I came across surprising behavior when dealing with numpy array slicing and views, as explained in:

Scipy wiki page on copying numpy arrays

I’ve stumbled across the following behavior, which is unexpected for me:

Case 1.:

import numpy as np
a = np.ones((3,3))
b = a[:,1:2]
b += 5
print a
print b.base is a

As expected, this outputs:

array([[ 1.,  6.,  1.],
       [ 1.,  6.,  1.],
       [ 1.,  6.,  1.]])
True

Case 2: When performing the slicing and addition in one line, things look different:

import numpy as np
a = np.ones((3,3))
b = a[:,1:2] + 5
print a
print b.base is a

The part that’s surprising to me is that a[:,1:2] does not seem to create a view, which is then used as a left hand side argument, so, this outputs:

array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
False

Maybe someone can shed some light on why these two cases are different, I think I’m missing something.

Solution: I missed the obvious fact that the "+" operator, other than the in-place operator "+=" will always create a copy, so it’s in fact not related but slicing other than how in-place operators are defined for numpy arrays.

To illustrate this, the following generates the same output as Case 2:

import numpy as np
a = np.ones((3,3))
b = a[:,1:2]
b = b + 5
print a
print b.base is a
Asked By: soramimo

||

Answers:

The above is no different than:

>>> a=np.arange(5)
>>> b=a
>>> b
array([0, 1, 2, 3, 4])

>>> b+=5
>>> a
array([5, 6, 7, 8, 9])
>>> b
array([5, 6, 7, 8, 9])

>>> b=b+5
>>> b
array([10, 11, 12, 13, 14])
>>> a
array([5, 6, 7, 8, 9])

Which, at least to me, seem like completely expected behavior. The b+=x operator calls __iadd__ which importantly first tries to modify the array in place, so it will update b which is still a view of a. While the b=b+x operator calls __add__ which creates new temporary data and then assigns it to b.

For a[i] +=b the sequence is (in numpy):

a.__setitem__(i, a.__getitem__(i).__iadd__(b))
Answered By: Daniel

The default thing to do when slicing a numpy array is to create b as a view of a, thus when you change b, a changes as well, which is confirmed by your first case.

The second case is more tricky. You are not telling that b is a slice of a and then adding a number. What you are doing is creating b as something that does not coincide with a, so numpy is forced to copy the data instead of just creating a view.

Answered By: Jblasco

a[:, 1:2] creates a view, but you don’t modify the view in the second example. Instead, + creates a new array from its arguments. Suppose you do

a = np.ones((3, 3))
b = a + 5

In that case, you wouldn’t expect a change to a, because this isn’t an in-place addition. The operator is +, rather than +=. It’s the same with the second example.

b = a[:, 1:2] + 5

doesn’t modify a[:, 1:2], because this isn’t in-place addition.

Answered By: user2357112
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.