15

I have 3 lists:

a = [True, False, True]
b = [False, False, True]
c = [True, True, False]

When I type

a or b or c

I want to get back a list that's

[True, True, True]

but I'm getting back

[True, False, True]

Any ideas on why? And how can I combine these masks?

bard
  • 2,762
  • 9
  • 35
  • 49

8 Answers8

13

Your or operators are comparing the lists as entire objects, not their elements. Since a is not an empty list, it evaluates as true, and becomes the result of the or. b and c are not even evaluated.

To produce the logical OR of the three lists position-wise, you have to iterate over their contents and OR the values at each position. To convert a bunch of iterables into a list of their grouped elements, use zip(). To check if any element in an iterable is true (the OR of its entire contents), use any(). Do these two at once with a list comprehension:

mask = [any(tup) for tup in zip(a, b, c)]
jscs
  • 63,694
  • 13
  • 151
  • 195
  • 4
    Great answer, thanks! In case anyone is wondering, to check if all elements are True, use all() instead of any(). – bard Nov 17 '13 at 04:31
13

How about this:

from numpy import asarray as ar

a = [True, False, True]
b = [False, False, True]
c = [True, True, False]

Try:

>>> ar(a) | ar(b) | ar(c)                  #note also the use `|` instead of `or`
array([ True, True, True], dtype=bool)

So no need for zip etc.

jscs
  • 63,694
  • 13
  • 151
  • 195
Developer
  • 8,258
  • 8
  • 49
  • 58
  • You need to convert back from `array` to `list` at the end there, but definitely a nice idea! – jscs Nov 17 '13 at 21:28
  • 1
    Good alternative solution for those who are already using numpy data structures. It's probably not worth the conversion from list->array->list (plus the overhead of loading the numpy module) just for this. – IceArdor Nov 20 '13 at 08:52
7

or returns the first operand if it evaluates as true, and a non-empty list evaluates as true; so, a or b or c will always return a if it's a non-empty list.

Probably you want

[any(t) for t in zip(a, b, c)]

(this works also for element-wise and if you replace any with all)

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
3

a is treated as true because it contains values; b, c is not evaluated.

>>> bool([])
False
>>> bool([True])
True
>>> bool([False])
True

>>> [False] or [True]
[False]

According to Boolean Operations:

The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

falsetru
  • 357,413
  • 63
  • 732
  • 636
2

Try this:

a = [True, False, True]
b = [False, False, True]
c = [True, True, False]

res = [a[i] or b[i] or c[i] for i in range(len(a))]
print res
Christian Tapia
  • 33,620
  • 7
  • 56
  • 73
  • 6
    Iterate directly rather than by index: `[any(tup) for tup in zip(a, b, c)]` – jscs Nov 17 '13 at 04:22
  • Rather than iterating by index (not pythonic), use either zip(a,b,c) or map(None,a,b,c) (if a,b,c are different sizes). – IceArdor Nov 20 '13 at 08:56
2

Here is how to do it fast (large arrays) using numpy:

import numpy as np
a = [True, False, True,...]
b = [False, False, True,...]
c = [True, True, False,...]
res = (np.column_stack((a,b,c)).any(axis=1)
print res

Note that a becomes the first column, b the second, and so on when using np.column_stack(). Then do a np.any() (logical OR) on that array along axis=1 which will compare the first elements of a,b, and c, and so on, and so on; resulting in a boolean vector that is the same length as the vectors you want to compare.

retox
  • 21
  • 2
2

how about

result = numpy.logical_or(a, b, c)
print(result)
shahar_m
  • 3,461
  • 5
  • 41
  • 61
1

This is a good place to use one of python's asterisk functions (*args, **kwargs), which will allow you to pass 3 or 300000 lists.

    a = [True, False, True]
    b = [False, False, True]
    c = [True, True, False]
    data_lists = [a,b,c]

Here you use * to expand that list as arguments, zip to rearrange your data into columns (as lists), and any to check if any cell in that column is True.

    [any(l) for l in zip(*data_lists)]
    [True, True, True]

If you're working with a lot of lists it's the same, e.g.

    import numpy as np
    data_lists =  [[True if (i == j) else False for i in range(7)] for j in range(7)]
    np.matrix(data_lists)
    matrix([[ True, False, False, False, False, False, False],
            [False,  True, False, False, False, False, False],
            [False, False,  True, False, False, False, False],
            [False, False, False,  True, False, False, False],
            [False, False, False, False,  True, False, False],
            [False, False, False, False, False,  True, False],
            [False, False, False, False, False, False,  True]])
    [any(l) for l in zip(*data_lists)]
    [True, True, True, True, True, True, True]
rer
  • 1,198
  • 2
  • 13
  • 24