Element-wise 'and' for lists in python?

I have two lists:

X = [True,False] Y = [True,True] 

I am trying to compare X [0] with Y [0] and X [1] with Y [1].

I tried

 in [7]: X and Y Out[7]: [True, True] 

but the expected result was [True, False].

What should I do?

+4
source share
3 answers

This is a great opportunity to use map , because and can be represented with a built-in function:

 import operator X = [True,False] Y = [True,True] map(operator.and_, X,Y) #=> [True, False] 

The reason you execute your behavior is because and performs operations on operands as if they applied bool to them. All non-empty lists are evaluated in True in a boolean context.

As for β€œlist comprehension is always better”: no. The concept of an equivalent list:

 [x and y for x, y in zip(X, Y)] 

Who needs to create an intermediate object (either a list or a generator, depending on the version of python), and still requires the reader to know what zip does, how map does. It will also probably be a little slower (because the map + function is built-in quickly - all this happens in the C layer, essentially). In fact, timeit shows that izip is faster (see below), but I really think that the readability point is more important; you can also see different results if performance really matters.

 >>> timeit.timeit('map(operator.and_, X,Y)', 'import operator; import itertools; import random; X = [random.choice([True,False]) for _ in range(1000)]; Y = [random.choice([True,False]) for _ in range(1000)]', number=10000) 1.0160579681396484 >>> timeit.timeit('[x and y for x, y in zip(X, Y)]', 'import operator; import itertools; import random; X = [random.choice([True,False]) for _ in range(1000)]; Y = [random.choice([True,False]) for _ in range(1000)]', number=10000) 1.3570780754089355 >>> timeit.timeit('[x and y for x, y in itertools.izip(X, Y)]', 'import operator; import itertools; import random; X = [random.choice([True,False]) for _ in range(1000)]; Y = [random.choice([True,False]) for _ in range(1000)]', number=10000) 0.965054988861084 

However, if you need an arbitrary number of lists, you need to use all in understanding the list (or in combination with izip directly); and and_ technically bitwise, so keep in mind that they may have funky results when dealing with numeric types other than bool .

Here is the version of all :

 import itertools map(all,itertools.izip(X,Y,Z)) 
+14
source

All non-empty lists are evaluated in True in a Boolean context, and and evaluates the last expression that it evaluates to ( Y in this case), so you get the result that you do. You want something like this:

 [x and y for x, y in zip(X, Y)] 
+9
source

Suppose you have an arbitrary group of lists:

 A=[True, False, False] B=[True, True, False] C=[3,0,0] 

Now write something similar to itertools.izip , but lets add a function:

 def elements(*iterables, **kwds): func=kwds.get('func', None) iterables=map(iter, iterables) while iterables: t=tuple(map(next, iterables)) if func is not None: yield func(t) else: yield t 

Now add functions that return the logical result F(A[0],B[0],C[0]...) . For example, each of them performs the described function:

 def ands(elements): ''' logical 'and' for all the elements''' return all(elements) def ors(elements): ''' logical 'or' for all the elements''' return any(elements) def bitand(elements): ''' bitwise 'and' for all the elements''' return reduce(operator.and_,elements) 

Then just call the function:

 print list(elements(A,B,C,func=ands)) # [True, False, False] 

Or for a specific example:

 print list(elements([True,False],[True,True],func=ands)) # [True, False] 

Or just use all directly:

 print list(elements([True,False],[True,True],func=all)) # [True, False] 
0
source

Source: https://habr.com/ru/post/1502477/


All Articles