7

I've got a 2D array comprised of boolean values (True,False). I'd like to consolidate the array to a 1D based on a logical function of the contents.

e.g. Input:

[[True, True, False],
 [False, False, False],
 [True, True, True]]

Output (logical AND):

[False,
 False,
 True]

How would this be done without a loop ?

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
DnRng
  • 167
  • 2
  • 8
  • Why without a loop? Loops are made to iterate over variable-length iterables, so if you do it without a loop you would lose the "variable-length" benefit (and have to hardcode the indices). Or are you just after an approach that hides the loop from you? – MSeifert Oct 16 '18 at 05:32

5 Answers5

9

You can use Python's built-in all method with a list-comprehension:

[all(x) for x in my_list]

If that's still too loopy for you, combine it with map:

map(all, my_list)

Note that map doesn't return a list in Python 3. If you want a list as your result, you can call list(map(all, my_list)) instead.

Woody1193
  • 7,252
  • 5
  • 40
  • 90
5

You can do this without NumPy too. Here is one solution using list comprehension. Explanation: It will loop over sub-lists and even if one of the items in each sub-list is False, it outputs False else True.

inp = [[True, True, False],[False, False, False],[True, True, True]]
out = [False if False in i else True for i in inp]
print (out)

# [False, False, True]

Alternative (less verbose) as suggested by Jean below:

out = [False not in i for i in inp]
Sheldore
  • 37,862
  • 7
  • 57
  • 71
  • This is clever but not exactly equivalent to `x1 and x2 and ... xn` for each sublist when you can have other falsy values in the sublists (such as `0`). Of course, OP explicitly said boolean values, so this is just a sidenote. – timgeb Oct 15 '18 at 20:08
  • Well the OP said it’s only True or False so my sokution addressed that – Sheldore Oct 15 '18 at 20:40
4

I'm assuming you want to apply logical ANDs to the rows. You can apply numpy.all.

>>> import numpy as np
>>> a = np.array([[True, True, False], [False, False, False], [True, True, True]])
>>> a
array([[ True,  True, False],
       [False, False, False],
       [ True,  True,  True]])
>>> 
>>> np.all(a, axis=1)
array([False, False,  True])

For a solution without numpy, you can use operator.and_ and functools.reduce.

>>> from operator import and_
>>> from functools import reduce
>>> 
>>> lst = [[True, True, False], [False, False, False], [True, True, True]]
>>> [reduce(and_, sub) for sub in lst]
[False, False, True]

edit: actually, reduce is a bit redundant in this particular case.

>>> [all(sub) for sub in lst]
[False, False, True]

does the job just as well.

timgeb
  • 76,762
  • 20
  • 123
  • 145
3

You can do this with with the numpy.all function:

>>> import numpy as np
>>> arr = np.array([[True, True, False],
... [False, False, False],
... [True, True, True]]
... )
>>> np.all(arr, axis=1)
array([False, False,  True])

Here thus the i-th element is True if all elements of the i-th row are True, and False otherwise. Note that the list should be rectangular (all sublists should contain the same number of booleans).

In "pure" Python, you can use the all function as well, like:

>>> data = [[True, True, False], [False, False, False], [True, True, True]]
>>> list(map(all, data))
[False, False, True]

This approach will work as well if the "matrix" is not rectangular. Note that for an empty sublist, this will return True, since all elements in an empty sublist are True.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
3

You can also do this with map and reduce:

from functools import reduce

l = [[True, True, False],
    [False, False, False],
    [True, True, True]]

final = list(map(lambda x: reduce(lambda a, b: a and b, x), l))
print(final)
# [False, False, True]

The benefit here is that you can change the reduce function to something else (say, an OR or something more adventurous).

slider
  • 12,810
  • 1
  • 26
  • 42