Comparing and flipping adjacent values in numpy - python

I want to compare adjacent values in a (potentially multi-dimensional) bool numpy array such that if there are adjacent True values in a row, only the leftmost would be kept while the rest would be flipped to False. For example:
Input: [True, False, False, True]
Output: [True, False, False, True]
Input: [True, True, False, True]
Output: [True, False, False, True]
Input: [True, True, True, True]
Output: [True, False, False, False]
Is there an efficient (i.e. vectorized) way of achieving this in NumPy, SciPy, or TensorFlow?

You can calculate the logical_and of the array with its shifted version, if both true, flip the values:
a[np.concatenate(([False], a[:-1])) & a] = False
Testing:
a = np.array([True, True, True, True])
a[np.concatenate(([False], a[:-1])) & a] = False
a
# array([ True, False, False, False], dtype=bool)
a = np.array([True, True, False, True])
a[np.concatenate(([False], a[:-1])) & a] = False
a
# array([ True, False, False, True], dtype=bool)
a = np.array([True, False, False, True])
a[np.concatenate(([False], a[:-1])) & a] = False
a
# array([ True, False, False, True], dtype=bool)

For a 1-d array:
a = np.array([True, True, False, True])
b = np.diff(a)
a[1:] = np.logical_and(a[1:], b)
>>> a
array([ True, False, False, True], dtype=bool)
>>>

Related

How do I combine multiple NumPy boolean arrays?

I have a two-dimensional (2D) array that contains many one-dimensional (1D) arrays of random boolean values.
import numpy as np
def random_array_of_bools():
return np.random.choice(a=[False, True], size=5)
boolean_arrays = np.array([
random_array_of_bools(),
random_array_of_bools(),
... so on
])
Assume that I have three arrays:
[True, False, True, True, False]
[False, True, True, True, True]
[True, True, True, False, False]
This is my desired result:
[False, False, True, False, False]
How can I achieve this with NumPy?
Use min with axis=0:
>>> boolean_array.min(axis=0)
array([False, False, True, False, False])
>>>
Use .all:
import numpy as np
arr = np.array([[True, False, True, True, False],
[False, True, True, True, True],
[True, True, True, False, False]])
res = arr.all(0)
print(res)
Output
[False False True False False]
try numpy bitwise_and =>
out_arr = np.bitwise_and(np.bitwise_and(in_arr1, in_arr2),in_arr3)

Split cells in two and and form np.array

list(itertools.product([[True,False],[False, True]], repeat=2))
The command above produces the output below.
[((True, False), (True, False)),
((True, False), (False, True)),
((False, True), (True, False)),
((False, True), (False, True))]
I want it however to look like this:
array([[ True, False, True, False],
[ True, False, False, True],
[False, True, True, False],
[False, True, False, True]])
Anything will help!
First generate your product:
pr = list(itertools.product([[True, False], [False, True]], repeat=2))
Then convert it to a Numpy array, with proper reshaping:
a = np.array(pr).reshape((len(pr), -1))
The result is:
array([[ True, False, True, False],
[ True, False, False, True],
[False, True, True, False],
[False, True, False, True]])
This code works also for other values of repeat (check e.g. for
repeat=3).
And a remark about other solutions: All of them, except for mathfux,
generate plain pythonic lists, not Numpy arrays (as you
specified).
The values of the product list are tuples.
You can loop over the tuples of the product list,
each time concatenate each list's tuples to a new list:
e.g.
([True, False], [True, False]) -> [True, False, True, False]
Option 1
import itertools
x = list(itertools.product([[True,False],[False, True]], repeat=2))
x = [list(v[0] + v[1]) for v in x]
# [[True, False, True, False], [True, False, False, True], [False, True, True, False], [False, True, False, True]]
print(x)
Option 2: One liner
import itertools
x = [list(v[0] + v[1]) for v in list(itertools.product([[True,False],[False, True]], repeat=2))]
# [[True, False, True, False], [True, False, False, True], [False, True, True, False], [False, True, False, True]]
print(x)
In numpy terms, you need to change its shape:
X = [((True, False), (True, False)),
((True, False), (False, True)),
((False, True), (True, False)),
((False, True), (False, True))]
np.array(X).reshape(4,4)
[[ True False True False]
[ True False False True]
[False True True False]
[False True False True]]
out = [[*a, *b] for a, b in itertools.product([[True,False],[False, True]], repeat=2)]
print(out)
Prints:
[[True, False, True, False],
[True, False, False, True],
[False, True, True, False],
[False, True, False, True]]

Numpy: Set false where anything to the left is false

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.
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)
>>> 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]]
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).

Compare a numpy array to each element of another one

A = np.array([5,1,5,8])
B = np.array([2,5])
I want to compare the A array to each element of B. In other words I'm lookin for a function which do the following computations :
A>2
A>5
(array([ True, False, True, True]), array([False, False, False, True]))
Not particularly fancy but a list comprehension will work:
[A > b for b in B]
[array([ True, False, True, True], dtype=bool),
array([False, False, False, True], dtype=bool)]
You can also use np.greater(), which requires the dimension-adding trick that Brenlla uses in the comments:
np.greater(A, B[:,np.newaxis])
array([[ True, False, True, True],
[False, False, False, True]], dtype=bool)

Intersect two boolean arrays for True

Having the numpy arrays
a = np.array([ True, False, False, True, False], dtype=bool)
b = np.array([False, True, True, True, False], dtype=bool)
how can I make the intersection of the two so that only the True values match? I can do something like:
a == b
array([False, False, False, True, True], dtype=bool)
but the last item is True (understandably because both are False), whereas I would like the result array to be True only in the 4th element, something like:
array([False, False, False, True, False], dtype=bool)
Numpy provides logical_and() for that purpose:
a = np.array([ True, False, False, True, False], dtype=bool)
b = np.array([False, True, True, True, False], dtype=bool)
c = np.logical_and(a, b)
# array([False, False, False, True, False], dtype=bool)
More at Numpy Logical operations.

Categories