1

How can I set values in the first 3 channels of a 4 channel numpy array based on values in the 4th channel? Is it possible to do so with a numpy slice as a l-value?

Given a 3 by 2 pixel numpy array with 4 channels

a = np.arange(24).reshape(3,2,4)
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],
       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]],
       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])

I can select slices where the 4th channel is modulo 3.

px = np.where(0==a[:,:,3]%3)
(array([0, 1], dtype=int64), array([0, 1], dtype=int64))

a[px]
array([[ 0,  1,  2,  3],
       [12, 13, 14, 15]])

Now I want to set the first 3 channels in those rows in a to 0 such that the results looks like:

a
array([[[ 0,  0,  0,  3],
        [ 4,  5,  6,  7]],
       [[ 8,  9, 10, 11],
        [ 0,  0,  0, 15]],
       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])

I tried

a[px][:,0:3] = 0

but that leaves the array unchanged.

I read Setting values in a numpy arrays indexed by a slice and two boolean arrays and do not understand how to use a Boolean index to set only the first 3 channels.

SpeedCoder5
  • 8,188
  • 6
  • 33
  • 34

1 Answers1

1

Here is one way:

>>> px0, px1 = np.where(0==a[:,:,3]%3)
>>> a[px0, px1, :3] = 0
>>> a
array([[[ 0,  0,  0,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [ 0,  0,  0, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])

or

>>> px = np.where(0==a[:,:,3]%3)
>>> a[..., :3][px] = 0
>>> a
array([[[ 0,  0,  0,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [ 0,  0,  0, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])

or

>>> a[(*px, np.s_[:3])] = 0
>>> a
array([[[ 0,  0,  0,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [ 0,  0,  0, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])
Paul Panzer
  • 51,835
  • 3
  • 54
  • 99
  • Thanks. You must be a numpy god. Yes. `a[px[0],px[1],:3] = 0`, `a[:,:,:3][px] = 0`, and `a[(px[0], px[1], np.s_[:3])] = 0` work too. The [Ellipsis syntax](https://stackoverflow.com/a/773472) and unpacking to a tuple with [s_ slice syntax](https://docs.scipy.org/doc/numpy/reference/generated/numpy.s_.html) are pretty cool. Why does `a[px][:,0:3] = 0` not work? What principle am I missing? – SpeedCoder5 Mar 07 '19 at 14:02
  • 1
    It's a bit tricky. `a[px] = 0` is syntactic sugar for `a.__setitem__(px, 0)` so just works. `a[px][:, :3] = 0` is `a.__getitem__(px).__setitem__((slice(None), slice(3)), 0)`. As `px` instructs fancy indexing, `__getitem__` copies the referenced data. As the subsequent `__setitem__` writes to this copy, `a` is not modified. If we do `[..., :3]` first and then `[px]` it looks similar with one crucial difference. The arg to `__getitem__` is a slice, hence it returns a view into the original data buffer of `a`. So `__setitem__` by writing into this view automatically also affects `a`. – Paul Panzer Mar 07 '19 at 15:22