简体   繁体   中英

Python numpy array integer indexed flat slice assignment

Was experimenting with numpy and found this strange behavior. This code works ok:

>>> a = np.array([[1, 2, 3], [4, 5, 6]])    
>>> a[:, 1].flat[:] = np.array([-1, -1])
>>> a 
array([[ 1, -1,  3],
       [ 4, -1,  6]])

But why this code doesn't change to -1 elements of 0 and 2 column?

>>> a[:, [0, 2]].flat[:] = np.array([-1, -1])
>>> a 
array([[ 1, -1,  3],
       [ 4, -1,  6]])

And how to write the code so that would change to -1 elements of 0 and 2 columns like this?

UPD: use of flat or smt similar is necessarily in my example

UPD2: I made example in question basing on this code:

img = imread(img_name)
xor_mask = np.zeros_like(img, dtype=np.bool)
# msg_bits looks like array([ True, False, False, ..., False, False,  True], dtype=bool)
xor_mask[:, :, channel].flat[:len(msg_bits)] = np.ones_like(msg_bits, dtype=np.bool)

And after assignment to xor mask with channel == 0 or 1 or 2 code works ok, but if channel == [1,2] or smt like this, assignment does not happen

In first example by flattening the slice you don't change the shape and actually the python Numpy doesn't create a new object. so assigning to flattened slice is like assigning to actual slice. But by flattening a 2d array you're changing the shape and hence numpy makes a copy of it.

also you don't need to flatten your slice to add to it:

In [5]: a[:, [0, 2]] += 100

In [6]: a
Out[6]: 
array([[101,   2, 103],
       [104,   5, 106]])

As others has pointed out .flat may create a copy of the original vector, so any updates to it would be lost. But flat tening a 1D slice is fine, so you can use a for loop to update multiple indexes.

import numpy as np

a = np.array([[1, 2, 3], [4, 5, 6]])
a[:, 1].flat = np.array([-1, -1])
print a

# Use for loop to avoid copies
for idx in [0, 2]:
     a[:, idx].flat = np.array([-1, -1])
print a

Note that you don't need to use flat[:] : just flat is enough (and probably more efficient).

You could just remove the flat[:] from a[:, [0, 2]].flat[:] += 100 :

>>> import numpy as np
>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> a[:, 1].flat[:] += 100
>>> a
array([[  1, 102,   3],
       [  4, 105,   6]])
>>> a[:, [0, 2]] += 100
>>> a
array([[101, 102, 103],
       [104, 105, 106]])

But you say it is necessary... Can't you just reshape whatever you are trying to add to the initial array instead of using flat ?

The second index call makes a copy of the array while the first returns a reference to it:

>>> import numpy as np
>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> b = a[:,1].flat
>>> b[0] += 100
>>> a
array([[  1, 102,   3],
       [  4,   5,   6]])
>>> b =a[:,[0,2]].flat
>>> b[0]
1
>>> b[0] += 100
>>> a
array([[  1, 102,   3],
       [  4,   5,   6]])
>>> b[:]
array([101,   3,   4,   6])

It appears that when the elements you wish to iterate upon in a flat maner are not adjacent numpy makes an iterator over a copy of the array.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM