Numpy: Set false where anything to the left is false

Viewed 175

TLDR; How do I set values in a numpy array dependent on values in columns to the left of each value...?

I am running some simulations where I am predicting survival rates, but below is the core of what I'm trying to do. I predict a bunch of discrete point in time survivals, represented as True and Falses. Each row is a simulation, and each column is a point in time (i.e. col 0 is the first point in time, col 1 is subsequent to that)

mc = (8, 4)
survival = np.random.random(mc) > np.random.random(mc)
survival

This will give me output like this.

array([[False,  True,  True, False],
       [True,  False,  True, False],
       [ True, True,  True,  True], ...

But if something dies in the first point in time, it is dead forever. So my output needs to be:

array([[False,  False,  False, False],
       [True,  False,  False, False],
       [ True, True,  True,  True], ...

So for a row, I want to set everything False to the right of the first False I find. Is there a way to do this without two nested loops? I'm looking for a better approach but struggling to know if I can do this with built-in functions.

4 Answers

The perfect tool exists :

np.logical_and.accumulate(survival,axis=1)

Example :

array([[False,  True, False,  True],
       [ True,  True, False,  True],
       [False,  True,  True,  True],
       [False,  True, False, False],
       [ True, False, False, False],
       [False,  True,  True,  True],
       [False, False,  True, False],
       [False, False,  True,  True]])

=>
array([[False, False, False, False],
       [ True,  True, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [ True, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False]])

Try not to use pure for loops when working with numpy arrays.

Use instead cumulative product against axis=1

arr.cumprod(1).astype(np.bool)

By using np.argwhere:

import numpy as np


bob = np.array([[True,True,False,True,True],[True,True,False,True,True],[False,True,True,True,True],[True,True,False,True,True],[False,True,True,True,True]]) 

for arr in np.argwhere(bob == False):
    bob[arr[0],arr[1]:] = False

the above argwhere returns for each instance of false the row,column, i use those value to set the rest of the row to false (after each false).

>>> mc = (8, 4)
>>> survival = np.random.random(mc) > np.random.random(mc)
>>> survival
array([[ True,  True,  True,  True],
       [ True, False, False,  True],
       [ True, False,  True,  True],
       [ True, False,  True, False],
       [False,  True, False, False],
       [ True,  True, False,  True],
       [ True,  True, False, False],
       [False, False,  True,  True]])

and

>>> death = [x.tolist().index(False) if False in x else -1 for x in survival]
>>> [s[ : d].tolist() + [False] * (survival.shape[1] - d) if d != -1 else s.tolist() for s, d in zip(survival, death)]
[[True, True, True, True],
 [True, False, False, False],
 [True, False, False, False],
 [True, False, False, False],
 [False, False, False, False],
 [True, True, False, False],
 [True, True, False, False],
 [False, False, False, False]]
Related