Logical operation between two Boolean lists - python

I get a weird result and I try to apply the and or the or operator to 2 Boolean lists in python. I actually get the exact opposite of what I was expecting.
[True, False, False] and [True, True, False]
> [True, True, False]
[True, False, False] or [True, True, False]
> [True, False, False]
Is that normal, and if yes, why?

If what you actually wanted was element-wise boolean operations between your two lists, consider using the numpy module:
>>> import numpy as np
>>> a = np.array([True, False, False])
>>> b = np.array([True, True, False])
>>> a & b
array([ True, False, False], dtype=bool)
>>> a | b
array([ True, True, False], dtype=bool)

This is normal, because and and or actually evaluate to one of their operands. x and y is like
def and(x, y):
if x:
return y
return x
while x or y is like
def or(x, y):
if x:
return x
return y
Since both of your lists contain values, they are both "truthy" so and evaluates to the second operand, and or evaluates to the first.

I think you need something like this:
[x and y for x, y in zip([True, False, False], [True, True, False])]

Both lists are truthy because they are non-empty.
Both and and or return the operand that decided the operation's value.
If the left side of and is truthy, then it must evaluate the right side, because it could be falsy, which would make the entire operation false (false and anything is false). Therefore, it returns the right side.
If the left side of or is truthy, it does not need to evaluate the right side, because it already knows that the expression is true (true or anything is true). So it returns the left side.
If you wish to perform pairwise comparisons of items in the list, use a list comprehension, e.g.:
[x or y for (x, y) in zip(a, b)] # a and b are your lists

Your lists aren't comparing each individual value, they're comparing the existence of values in the list.
For any truthy variables a and b:
a and b
> b #The program evaluates a, a is truthy, it evaluates b, b is truthy, so it returns the last evaluated value, b.
a or b
> a #The program evaluates a, a is truthy, so the or statement is true, so it returns the last evaluated value, a.
Now, truthy depends on the type. For example, integers are truthy for my_int != 0, and are falsy for my_int == 0. So if you have:
a = 0
b = 1
a or b
> b #The program evaluates a, a is falsy, so the or statement goes on to evaluate b, b is truthy, so the or statement is true and it returns the last evaluated value b.

Very convenient way:
>>> import numpy as np
>>> np.logical_and([True, False, False], [True, True, False])
array([ True, False, False], dtype=bool)
>>> np.logical_or([True, False, False], [True, True, False])
array([ True, True, False], dtype=bool)

Мore functional:
from operator import or_, and_
from itertools import starmap
a = [True, False, False]
b = [True, True, False]
starmap(or_, zip(a,b)) # [True, True, False]
starmap(and_, zip(a,b)) # [True, False, False]

Related

Numpy slice of a slice assignment fails unexpectedly

This attempt at a slice+assignment operation fails unexpectedly:
>>> x = np.array([True, True, True, True])
>>> x[x][0:2] = False
>>> x
array([ True, True, True, True])
I'd like to understand why the above simplified code snippet fails to assign the underlying array values.
Seemingly equivalent slicing+assignment operations do work, for example:
>>> x = np.array([True, True, True, True])
>>> x[0:4][0:2] = False
>>> x
array([False, False, True, True])
np.version.version == 1.17.0
The reason this will not work is because x[x] is not a "view", but a copy, and then you thus assign on a slice of that copy. But that copy is never saved. Indeed, if we evaluate x[x], then we see it has no base:
>>> x[x].base is None
True
We can however assign to the first two, or last five, etc. items, by first calculating the indices:
>>> x = np.array([True, True, True, True])
>>> x[np.where(x)[0][:2]] = False
>>> x
array([False, False, True, True])
Here np.where(x) will return a 1-tuple that contains the indices for which x is True:
>>> np.where(x)
(array([0, 1, 2, 3]),)
we then slice that array, and assign the indices of the sliced array.

How to return True if two-dimensional array in python has at least one True

I'm trying to write a code which checks if 2D-array (consists of only boolean) has at least one True and return True if there is at least one True.
I tried using all() function but couldn't come up with a solution. I assume what I need is opposite of what all() function does.
>>> array1 = [[True, False], [False, False]]
>>> all([all(row) for row in array1)
False # This should be True
>>> array2 = [[False, False], [False, False]]
>>> all([all(row) for row in array2)
False # This is correct but this and array with all True is only working case.
For array1 = [[True, False], [False, False]], I expect the output to be True since there is one True at array1[0][0].
def has_true(arr):
return any(any(row) for row in arr)
In [7]: array1 = [[True, False], [False, False]]
In [8]: array2 = [[False, False], [False, False]]
In [9]: has_true(array1)
Out[9]: True
In [10]: has_true(array2)
Out[10]: False
this answer is using generators so it will return upon finding the first True value without running over the whole matrix. in addition it will use O(1) space
edit: removed unnecessary code
use any() instead of all(). all() return true if all items in an iterable object are true.
any() Returns True if any item in an iterable object is true.
A much shorter approach is to chain the lists together using itertools.chain and apply any on them
from itertools import chain
def has_true(arr):
return any(chain(*arr))
print(has_true([[True, False], [False, False]]))
print(has_true([[False, False], [False, False]]))
The output will be
True
False

Python: numpy array larger and smaller than a value

How to look for numbers that is between a range?
c = array[2,3,4,5,6]
>>> c>3
>>> array([False, False, True, True, True]
However, when I give c in between two numbers, it return error
>>> 2<c<5
>>> ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
The desire output is
array([False, True, True, False, False]
Try this,
(c > 2) & (c < 5)
Result
array([False, True, True, False, False], dtype=bool)
Python evaluates 2<c<5 as (2<c) and (c<5) which would be valid, except the and keyword doesn't work as we would want with numpy arrays. (It attempts to cast each array to a single boolean, and that behavior can't be overridden, as discussed here.) So for a vectorized and operation with numpy arrays you need to do this:
(2<c) & (c<5)
You can do something like this :
import numpy as np
c = np.array([2,3,4,5,6])
output = [(i and j) for i, j in zip(c>2, c<5)]
Output :
[False, True, True, False, False]

Flipping the boolean values in a list Python

I have a boolean list in Python
mylist = [True , True, False,...]
which I want to change to the logical opposite [False, False, True , ...]
Is there an inbuilt way to do this in Python (something like a call not(mylist) ) without a hand-written loop to reverse the elements?
It's easy with list comprehension:
mylist = [True , True, False]
[not elem for elem in mylist]
yields
[False, False, True]
The unary tilde operator (~) will do this for a numpy.ndarray. So:
>>> import numpy
>>> mylist = [True, True, False]
>>> ~numpy.array(mylist)
array([False, False, True], dtype=bool)
>>> list(~numpy.array(mylist))
[False, False, True]
Note that the elements of the flipped list will be of type numpy.bool_ not bool.
>>> import operator
>>> mylist = [True , True, False]
>>> map(operator.not_, mylist)
[False, False, True]
Numpy includes this functionality explicitly. The function
"numpy.logical_not(x[, out])" computes the truth value of NOT x element-wise.
import numpy
numpy.logical_not(mylist)
http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.logical_not.html (with same examples)
Example:
import numpy
mylist = [True , True, False]
print (mylist)
returns [True, True, False]
mylist=numpy.logical_not(mylist)
print (mylist)
returns [False False True]
numpy.invert is another nice option:
x = [False, True, True]
not_x = np.invert(x) # [True, False, False]
Why not just use a simple list comprehension?
mylist[:] = [not x for x in mylist]
I would do it the way everybody else is saying, but for sake of documenting alternatives, you could also do
import operator
myList = map(operator.not_, myList)
what about the following
>>> import numpy
>>> list(numpy.asarray(mylist)==False)

How to construct logical expression for advanced slicing

I am trying to figure out a cleaner way of doing the following:
import numpy
a = np.array([1,2,4,5,1,4,2,1])
cut = a == (1 or 2)
print cut
[ True False False False True False False True]
The above is of course a simplified example. The expression (1 or 2) can be large or complicated. As a start, I would like to generalize this thusly:
cutexp = (1 or 2)
cut = a == cutexp
Maybe, cutexp can be turned into a function or something but I'm not sure where to start looking.
You could also try numpy.in1d. Say
>>> a = np.array([1,2,4,5,1,4,2,1])
>>> b = np.array([1,2]) # Your test array
>>> np.in1d(a,b)
array([ True, True, False, False, True, False, True, True], dtype=bool)
>>> (a == 2) | (a == 1)
array([ True, True, False, False, True, False, True, True], dtype=bool)

Categories