Why does an assignment for double-sliced numpy arrays not work?
Asked Answered
C

5

15

why do the following lines not work as I expect?

import numpy as np
a = np.array([0,1,2,1,1])
a[a==1][1:] = 3
print a
>>> [0 1 2 1 1]
# I would expect [0 1 2 3 3]

Is this a 'bug' or is there another recommended way to this?

On the other hand, the following works:

a[a==1] = 3
print a
>>> [0 3 2 3 3]

Cheers, Philipp

Corallite answered 6/11, 2009 at 13:21 Comment(0)
O
9

It appears you simply can't do an assignment through a double-slice like that.

This works though:

a[numpy.where(a==1)[0][1:]] = 3
Oceanic answered 6/11, 2009 at 13:53 Comment(0)
A
10

It's related to how fancy indexing works. There is a thorough explanation here. It is done this way to allow inplace modification with fancy indexing (ie a[x>3] *= 2). A consequence of this is that you can't assign to a double index as you have found. Fancy indexing always returns a copy rather than a view.

Addition answered 6/11, 2009 at 14:1 Comment(3)
Actually, your solution modifies the first occurrence of 1, which is not what he wants.Oceanic
Right - took it off before your comment. Ps hi Philip it's Robin!Addition
Hey Robin - what chance is that to meet here... Cheers from munich!Corallite
O
9

It appears you simply can't do an assignment through a double-slice like that.

This works though:

a[numpy.where(a==1)[0][1:]] = 3
Oceanic answered 6/11, 2009 at 13:53 Comment(0)
J
3

Because the a[a==1] part isn't actually a slice. It creates a new array. It makes sense when you think about it-- you're only taking the elements that satisfy the boolean condition (like a filter operation).

Jugum answered 6/11, 2009 at 13:36 Comment(6)
I added a second example that works like I expect. I don't really see the difference. Shouldn't the assignment being "piped through"?Corallite
I don't think this is quite right. If you do a[a==1] = 3, that actually changes the contents of a.Oceanic
@Dave - I think this is perimosocodiae is correct, and that your counter-example is due to something more like a hack in the numpy internals to create the appearance of an in-place operation.Incantatory
@Incantatory - It's not a "hack". It's part of the implementation of the array class. Behavior is documented, for instance, here: scipy.org/…. "Slicing an array returns a view of it", i.e. not a copy.Oceanic
normal list works that way too -- you can assign to slices (only with iterables) l = range(10); l[5:] = range(5)Lussi
@Dave - Certainly, a slice in numpy is a view, but a[a==1] is not a slice and not a view.Incantatory
J
0

This does what you want

a[2:][a[2:]==1]=3
Janijania answered 6/11, 2009 at 13:59 Comment(1)
But that requires knowing in advance that the first occurrence of 1 is at position 1.Oceanic
O
0

Depending on the conditions using np.where can be difficult. I would suggest creating an indexing array independently :

a = np.array([0,1,2,1,1])
pos_to_change = np.arange(0,len(a))[a==1][1:]
a[pos_to_change] = 3
print(a)
>>> array([0, 1, 2, 3, 3])
Ostracod answered 10/10, 2023 at 9:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.