Ivan Shelonik
Ivan Shelonik

Reputation: 2028

np.nditer doesn't assign values when iterates over subarray

Main array doesn't update values during assigning method inside np.nditer when iterative array is used as subarray

array = np.arange(20)

with np.nditer(array[np.nonzero(array)],
               op_flags=['readwrite']) as it:
    for x in it:
        x[...] = 5
array

array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])


Subarray assigns great without np.nditer

array[np.nonzero(array)] = 5
array

array([0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])


There is a workaround over by using temporary array.

tmp_array = array[np.nonzero(array)]

array = np.arange(20)

with np.nditer(tmp_array,
               op_flags=['readwrite']) as it:
    for x in it:
        x[...] = 5

array[np.nonzero(array)] = tmp_array
array

array([0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])

  1. Why main array doesn't update values when np.nditer uses subarray assignments?
  2. Is there a more convenient way to workaround when subarray assigning in np.nditer happens?

Upvotes: 0

Views: 365

Answers (1)

hpaulj
hpaulj

Reputation: 231425

In [48]: arr = np.arange(20) 
    ...: with np.nditer(arr[np.nonzero(arr)], 
    ...:                op_flags=['readwrite']) as it: 
    ...:     for x in it: 
    ...:         x[...] = 5 
    ...: arr                                                                    
Out[48]: 
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19])

This doesn't change arr because it isn't iterating on arr. Instead it is doing:

In [49]: arr = np.arange(20) 
    ...: arr1 = arr[np.nonzero(arr)] 
    ...: with np.nditer(arr1, 
    ...:                op_flags=['readwrite']) as it: 
    ...:     for x in it: 
    ...:         x[...] = 5 
    ...: arr1                                                                   
Out[49]: array([5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])
In [50]: _.shape                                                                
Out[50]: (19,)

arr1 isn't a view of arr, it is selection, a new array without shared data buffer.

If you want to modify arr, you have to iterate on it, not a copy. Do the test, or anything fancy, inside the loop.

In [51]: arr = np.arange(20) 
    ...: with np.nditer(arr, 
    ...:                op_flags=['readwrite']) as it: 
    ...:     for x in it: 
    ...:         if x > 0: 
    ...:             x[...] = 5 
    ...: arr                                                                    
Out[51]: array([0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])

But why are you using nditer? You already know how to do this kind of selective assignment, using the non-iterative methods:

array[np.nonzero(array)] = 5

In this case the assignment = immediately follows the advanced indexing, and Python uses array.__setitem__ rather than arr.__getitem__.

Iterating on a slice, a view does change the source:

In [52]: arr = np.arange(20) 
    ...: with np.nditer(arr[5:], 
    ...:                op_flags=['readwrite']) as it: 
    ...:     for x in it: 
    ...:         x[...] = 5 
    ...: arr                                                                    
Out[52]: array([0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])

Upvotes: 1

Related Questions