Replacing numpy array elements with chained masks

Viewed 167

Consider some array arr and advanced indexing mask mask:

import numpy as np

arr = np.arange(4).reshape(2, 2)
mask = A < 2

Using advanced indexing creates a new copy of an array. Accordingly, one cannot "chain" a mask with an an additional mask or even with a basic slicing operation to replace elements of an array:

submask = [False, True]
arr[mask][submask] = -1  # chaining 2 masks
arr[mask][:] = -1  # chaining a mask with a basic slicing operation

print(arr)
[[0 1]
 [2 3]]

I have two related questions:

1/ What is the best way to replace elements of an array using chained masks?

2/ If advanced indexing returns a copy of an array, why does the following work?

arr[mask] = -1

print(arr)
[[-1 -1]
 [ 2  3]]
2 Answers

Couple of good questions! My take:

  1. I would do something like this:
x,y=np.where(mask)
arr[x[submask],y[submask]] = -1
  1. From the official document:

Most of the following examples show the use of indexing when referencing data in an array. The examples work just as well when assigning to an array. See the section at the end for specific examples and explanations on how assignments work.

which means arr[mask]=1 is referrencing, while arr[mask] is extracting data and creates a copy.

The short answer:

  • you have to figure out a way of combining the masks. Since masks can "chain" in different ways I don't think there's a simple all-purpose substitute.

  • indexing can either be a __getitem__ call, or a __setitem__. Your last case is a set.

With chained indexing, a[mask1][mask2] =value gets translated into

a.__getitem__(mask1).__setitem__(mask2, value)

Whether a gets modified or not depends on what the first getitem produces (a view vs copy).

In [11]: arr = np.arange(4).reshape(2,2)
In [12]: mask = arr<2
In [13]: mask
Out[13]: 
array([[ True,  True],
       [False, False]])
In [14]: arr[mask]
Out[14]: array([0, 1])

Indexing with a list or array may preserve the number of dimensions, but a boolean like this returns a 1d array, the items where the mask is true.

In your example, we could tweak the mask (details may vary with the intent of the 2nd mask):

In [15]: mask[:,0]=False
In [16]: mask
Out[16]: 
array([[False,  True],
       [False, False]])
In [17]: arr[mask]
Out[17]: array([1])
In [18]: arr[mask] += 10
In [19]: arr
Out[19]: 
array([[ 0, 11],
       [ 2,  3]])

Or a logical combination of masks:

In [26]: (np.arange(4).reshape(2,2)<2)&[False,True]
Out[26]: 
array([[False,  True],
       [False, False]])
Related