Python boolean operations on lists - inconsistent results - python

Suppose I have two lists:
>>> y
[False, False, True, False, True, False, False, True, False, True, False, False]
>>> z
[False, True, True, True, True, True, False, False, False, False, False, True]
Then I do the following:
>>> y or z
[False, False, True, False, True, False, False, True, False, True, False, False]
>>> z or y
[False, True, True, True, True, True, False, False, False, False, False, True]
Shouldn't the correct answer be as shown below?
[False, True, True, True, True, True, False, True, False, True, False, True]
I also get incorrect answers with and:
>>> y and z
[False, True, True, True, True, True, False, False, False, False, False, True]
>>> z and y
[False, False, True, False, True, False, False, True, False, True, False, False]
I tested 1s and 0s with odd results:
>>> y=[0,0,0,0,0]
>>> z=[1,1,1,1,1]
>>> y or z
[0, 0, 0, 0, 0]
>>> z or y
[1, 1, 1, 1, 1]
>>> y and z
[1, 1, 1, 1, 1]
>>> z and y
[0, 0, 0, 0, 0]
What am I doing incorrectly?

y or z doesn't behave how you think it does, on the individual elements. Instead, it evaluates the 'truthiness' of the first argument (y). Since y is a non-empty list, it evaluates to true. The overall statement then evaluates to y.
Similarly, z or y first looks to see if z is truthy (which it is, because it's a non empty list). Thus the statement evaluates to z without ever looking at y or the elements within it.
Here are some clearer examples:
>>> [1,2,3,4] or [5,6,7,8]
[1, 2, 3, 4]
>>> ['this','is','a','list'] or ['and','here','is','another']
['this', 'is', 'a', 'list']
An empty list evaluates as 'false-y', so in this care, the right hand list is the value of the statement:
>>> [] or ['and','here','is','another']
['and', 'here', 'is', 'another']
Swapping the order of the lists shows that the first one to evaluate as true will be the result:
>>> ['and','here','is','another'] or ['this','is','a','list']
['and', 'here', 'is', 'another']
To achieve what you want, you could do a list comprehension like
[
y_item or z_item
for y_item, z_item
in zip(y, z)
]

The correct approach for or operation:
[a or b for a, b in zip(y, z)]
The correct approach for and operation:
[a and b for a, b in zip(y, z)]
None, False, 0, '', (), [], {} and few more (mentioned here -> Truth Value Testing) are considered False.
here's some example:
[] is False, [False] is True since its not empty, check using
bool([False])
>>> [] and [False]
[]
>>> bool([] and [False])
False
[] is False, [False] is True, hence Here True
>>> [] or [False]
[False]
>>> bool([] or [False])
True

Related

Subtract elements from one list from another list, using list comprehension. Returns incomplete list?

I have an array l1 of size (81x2), and another l2 of size (8x2). All elements of l2 are also contained in l1. I'm trying to generate an array l3 of size (73x2) containing all elements of l1 minus the ones in l2 ( ==> l3 = l1 - l2 ), but using list comprehension.
I found many similar questions on here, and almost all agree on a solution like this to generate l3:
n = 9
index = np.arange(n)
l1 = np.array([(i,j) for i in index for j in index])
l2 = np.array([(0, 3),(0, 5),(2, 4),(4, 4),(4, 2),(4, 6),(8, 3),(8, 5)])
l3 = [(i,j) for (i,j) in l1 if (i,j) not in l2]
print(l3)
However, the code above generates an array l3 that only contains 20 of the expected (81-8=) 73 elements. I don't understand how list comprehension operates here or why only those particular 20 elements are kept. Can anyone help?
NOTE: many people advise using set() instead of list comprehension for this problem, but I haven't tried that yet and I'd really like to understand why list comprehension is failing in the code above.
Let's test the first row of l1:
In [46]: i,j = l1[0]
In [47]: i,j
Out[47]: (0, 0)
In [48]: (i,j) in l2
Out[48]: True
It's True because 0 occurs in l2. It isn't testing by rows.
There isn't a 7 in l2, so this is False
In [49]: (7,7) in l2
Out[49]: False
Make sure your list comprehension test works.
One way to test for matches is:
In [72]: x = (l1==l2[:,None,:]).all(axis=2).any(axis=0)
In [73]: x
Out[73]:
array([False, False, False, True, False, True, False, False, False,
False, False, False, 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, True, False, True, False, True, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, True, False, True, False, False, False])
This has 8 True values, the ones that exactly match l2:
In [74]: x.sum()
Out[74]: 8
In [75]: l1[x]
Out[75]:
array([[0, 3],
[0, 5],
[2, 4],
[4, 2],
[4, 4],
[4, 6],
[8, 3],
[8, 5]])
So the rest would be accessed with:
In [76]: l1[~x]
TO work with sets, we need to convert the arrays to lists of tuples
In [85]: s1 = set([tuple(x) for x in l1])
In [86]: s2 = set([tuple(x) for x in l2])
In [87]: len(s1.difference(s2))
Out[87]: 73
Another approach is to convert the arrays to structured arrays:
In [88]: import np.lib.recfunctions as rf
In [102]: r1 = rf.unstructured_to_structured(l1,dtype=np.dtype('i,i'))
In [103]: r2 = rf.unstructured_to_structured(l2,dtype=np.dtype('i,i'))
In [104]: r2
Out[104]:
array([(0, 3), (0, 5), (2, 4), (4, 4), (4, 2), (4, 6), (8, 3), (8, 5)],
dtype=[('f0', '<i4'), ('f1', '<i4')])
Now isin works - the arrays are both 1d, as required by isin:
In [105]: np.isin(r1,r2)
Out[105]:
array([False, False, False, True, False, True, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, True, False, False, False, False,
...])

How to generate these lists of True and False?

I am looping through a list of 3 items, something like:
for i in range(3):
and trying to produce the following lists on each respective iteration:
[True, True, False, False, False, False]
[False, False, True, True, False, False]
[False, False, False, False, True, True]
What would be a good way in python to do this?
Here's one way:
>>> for i in range(3):
... print([(x // 2) == i for x in range(6)])
...
[True, True, False, False, False, False]
[False, False, True, True, False, False]
[False, False, False, False, True, True]
Try like this:
k = 0
for i in range(3):
# Other tasks
myList = [False for x in range(4)]
myList[k:k] = [True,True]
print(myList)
k += 2
L = [False, False, False, False, True, True]
for _ in range(3):
L = L[-2:] + L[:4]
print(L)

Choose random elements from specific elements of an array

I have a 1D (numpy) array with boolean values. for example:
x = [True, True, False, False, False, True, False, True, True, True, False, True, True, False]
The array contains 8 True values. I would like to keep, for example, exactly 3 (must be less than 8 in this case) as True values randomly from the 8 that exist. In other words I would like to randomly set 5 of those 8 True values as False.
A possible result can be:
x = [True, True, False, False, False, False, False, False, False, False, False, False, True, False]
How to implement it?
One approach would be -
# Get the indices of True values
idx = np.flatnonzero(x)
# Get unique indices of length 3 less than the number of indices and
# set those in x as False
x[np.random.choice(idx, len(idx)-3, replace=0)] = 0
Sample run -
# Input array
In [79]: x
Out[79]:
array([ True, True, False, False, False, True, False, True, True,
True, False, True, True, False], dtype=bool)
# Get indices
In [80]: idx = np.flatnonzero(x)
# Set 3 minus number of True indices as False
In [81]: x[np.random.choice(idx, len(idx)-3, replace=0)] = 0
# Verify output to have exactly three True values
In [82]: x
Out[82]:
array([ True, False, False, False, False, False, False, True, False,
False, False, True, False, False], dtype=bool)
Build an array with the number of desired True and False, then just shuffle it
import random
def buildRandomArray(size, numberOfTrues):
res = [False]*(size-numberOfTrues) + [True]*numberOfTrues
random.shuffle(res)
return res
Live example

Finding subset sum using dynamic programming

I'm practicing Dynamic Programming and I'm struggling with debugging my code. The idea is to find if a sum is possible given a list of numbers. Here's my code:
a = [2,3,7,8,10]
sum = 11
b = list(range(1, sum+1))
m = [[False for z in range(len(b))] for i in range(len(a))]
for i, x in enumerate(b):
for j, y in enumerate(a):
if x==y:
m[j][i]=True
elif y<x:
m[j][i] = m[j-1][i]
else:
m[j][i] = m[j-1][i] or m[j-i][y-x]
for i, n in enumerate(m):
print(a[i], n)
And here is the output:
2 [False, True, False, False, False, False, False, False, False, False, False]
3 [False, True, True, False, False, False, False, False, False, False, False]
7 [False, True, True, False, True, True, True, False, False, False, False]
8 [False, True, True, False, True, True, True, True, False, False, False]
10 [False, True, True, False, True, True, True, True, True, True, False]
As I understand it, in my else statement, the algorithm is supposed to go up 1 row and then look at the difference of x and y and check if that slot is possible. So for instance in the most obvious case, the last element in the last row. That would be 10(y)-11(x) which should go all the way back to index 1 on the row above it, which as we know it's True. Not entirely sure what I'm doing wrong, any help in understanding this would be greatly appreciated.
Given you only feed positive values, I don't quite follow why you need a two dimensional list. You can simply use a 1d list:
coins = [2,3,7,8,10]
sum = 11
Next we initialize the list possible that states whether it is possible to obtain a certain value. We set possible[0] to True since this sum can be accomplished with no coins.
possible = [False for _ in range(sum+1)]
possible[0] = True
Now you iterate over each coin, and over the list and "upgrade" the value if possible:
for coin in coins:
for i in range(sum-coin,-1,-1):
if possible[i]:
possible[i+coin] = True
After that, the list possible shows for each value from 0 up to (and including sum) whether you can construct it. So if possible[sum] is True, the sum can be constructed.
For the given coins and sum, one gets:
>>> possible
[True, False, True, True, False, True, False, True, True, True, True, True]
So values 0, 2, 3, 5, 7, 8, 9, 10, 11 are constructible with the coins.
Edit: track the coins
You can also keep track of the coins by slightly modifying the code:
possible = [None for _ in range(sum+1)]
possible[0] = []
for coin in coins:
for i in range(sum-coin,-1,-1):
if possible[i] is not None:
possible[i+coin] = possible[i]+[coin]
Now possible looks like:
>>> possible
[[], None, [2], [3], None, [2, 3], None, [7], [8], [2, 7], [10], [3, 8]]
So 0 can be constructed with coins [] (no coins); 2 can be constructed with [2] (one coin with value 2), 3 with [3], 5 with [2,3], etc.

Generate Truth Table but leave some variables undefined in python

input:
vv = [True, False, None, True, None]
Is there a way to generate truth table by replacing undefined one boolean at a time
So the ouptut would be
[True, False, True, True, None]
[True, False, False, True, None]
[True, False, None, True, False]
[True, False, None, True, True]
Assuming that Undefined can be None, for example, you could do as follows:
l = [True, False, None, True, None]
# first get indices of None/Undefined elements in the input list
indexes = [i for i,v in enumerate(l) if v is None]
# for each index, generate new list using True and False
# as substitutes.
for idx in indexes:
for sub in [True, False]:
new_l = l[:]
new_l[idx] = sub
print(new_l)
This gives:
[True, False, True, True, None]
[True, False, False, True, None]
[True, False, None, True, True]
[True, False, None, True, False]

Categories

Resources