4

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
3
  • If both results are "as expected", what are you confused about? Commented Aug 9, 2013 at 21:42
  • updated my question, the results from case 2 is unexpected, I would it expect to be the same as case 1. Commented Aug 9, 2013 at 21:47
  • 1
    The slice still creates a view; it's + that creates a new array. Commented Aug 9, 2013 at 21:56

3 Answers 3

3

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))
Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.