web-dev-qa-db-fra.com

Python ET opérateur sur deux listes booléennes - comment?

J'ai deux listes booléennes, par exemple,

x=[True,True,False,False]
y=[True,False,True,False]

Je veux ET ces listes ensemble, avec le résultat attendu:

xy=[True,False,False,False]

Je pensais que l'expression x and y fonctionnerait, mais j'ai découvert que ce n'était pas le cas: en fait, (x and y) != (y and x)

Sortie de x and y: [True,False,True,False]

Sortie de y and x: [True,True,False,False]

L'utilisation de la liste compréhension a a une sortie correcte. Ouf!

xy = [x[i] and y[i] for i in range(len(x)]

Remarquez que je n’ai trouvé aucune référence me indiquant que l’opérateur AND fonctionnerait comme j’ai essayé avec x et y. Mais il est facile d'essayer des choses en Python… .. Quelqu'un peut-il m'expliquer ce qui se passe avec x and y?

Et voici un programme de test simple:

import random
random.seed()
n = 10
x = [random.random() > 0.5 for i in range(n)]
y = [random.random() > 0.5 for i in range(n)]
# Next two methods look sensible, but do not work
a = x and y
z = y and x
# Next: apparently only the list comprehension method is correct
xy = [x[i] and y[i] for i in range(n)]
print 'x        : %s'%str(x)
print 'y        : %s'%str(y)
print 'x and y  : %s'%str(a)
print 'y and x  : %s'%str(z)
print '[x and y]: %s'%str(xy)
24
Glenn N

Vous pouvez utiliser numpy:

>>> import numpy as np
>>> x=np.array([True,True,False,False])
>>> y=np.array([True,False,True,False])
>>> x & y
array([ True, False, False, False], dtype=bool)

Numpy permet des opérations numériques et logiques sur des tableaux tels que:

>>> z=np.array([1,2,3,4])
>>> z+1
array([2, 3, 4, 5])

Vous pouvez effectuer des opérations au niveau des bits et avec l'opérateur &.

Au lieu d'une compréhension de liste, vous pouvez utiliser numpy pour générer le tableau booléen directement comme ceci:

>>> np.random.random(10)>.5
array([ True,  True,  True, False, False,  True,  True, False, False, False], dtype=bool)
6
dawg

and n'est pas nécessairement un opérateur booléen; il retourne l'un de ses deux arguments, quel que soit leur type. Si le premier argument est false-ish (False, zéro numérique ou une chaîne/un conteneur vide), cet argument est renvoyé. Sinon, il retourne le deuxième argument.

Dans votre cas, x et y sont des listes non vides. Le premier argument est donc toujours vrai, ce qui signifie que x and y renvoie y et y and x renvoie x.

4
chepner

Cela devrait faire ce que vous voulez:

xy = [a and b for a, b in Zip(x, y)]

La raison pour laquelle x and y renvoie y et y and x renvoie x est due au fait que les opérateurs booléens en python renvoient la dernière valeur vérifiée qui détermine la véracité de l'expression. La valeur list non vide est True, et comme and requiert les deux opérandes pour évaluer True, le dernier opérande coché est le deuxième. Contrastez avec x or y, qui renverrait x car il n'est pas nécessaire de vérifier y pour déterminer la véracité de l'expression.

2
Cyphase

À la place d'utiliser

[a and b for a, b in Zip(x, y)]

on pourrait simplement utiliser la possibilité de numpy pour multiplier bool-values:

(np.array(x)*np.array(y))
>> array([ True, False, False, False], dtype=bool)

Ou est-ce que je néglige un cas particulier?

1
Philipp

Merci pour la réponse @Martijn Pieters et @Tony . J'ai creusé dans le calendrier des différentes options permettant de créer le AND de deux listes et j'aimerais partager mes résultats, car je les ai trouvés intéressants.

Malgré avoir beaucoup aimé la manière pythonique [a et b pour a, b dans Zip (x, y)], s’avère très lent . Je compare avec un produit entier de tableaux (1 * (tableau de bool)) * (1 * (array of bool)) et il s'avère être plus de 10 fois plus rapide

import time
import numpy as np
array_to_filter = np.linspace(1,1000000,1000000)                # 1 million of integers :-)
value_limit = 100
cycles = 100

# METHOD #1:  [a and b for a,b in Zip(x,y) ]
t0=time.clock()
for jj in range(cycles):
    x = array_to_filter<np.max(array_to_filter)-value_limit   # filter the values > MAX-value_limit
    y = array_to_filter>value_limit                          # filter the values < value_limit
    z= [a and b for a,b in Zip(x,y) ]                       # AND
    filtered = array_to_filter[z]
print('METHOD #1 = %.2f s' % ( (time.clock()-t0)))



# METHOD 1*(array of bool) AND  1*(array of bool)
t0=time.clock()
for jj in range(cycles):
    x = 1*(array_to_filter<np.max(array_to_filter)-value_limit)   # filter the values > MAX-value_limit
    y = 1*(array_to_filter>value_limit)                          # filter the values < value_limit
    z = x*y                                                     # AND
    z = z.astype(bool)                                          # convert back to array of bool
    filtered = array_to_filter[z]
print('METHOD #2 = %.2f s' % ( (time.clock()-t0)))

Les résultats sont

METHOD #1 = 15.36 s
METHOD #2 = 1.85 s

La vitesse dépend presque autant de la taille de la matrice que du nombre de cycles.
J'espère avoir aidé quelqu'un à coder plus rapidement. :-)

0
Marco Triches

Vous pouvez utiliser la fonction Zip

x=[True,True,False,False]
y=[True,False,True,False]
z=[a and b for a,b in Zip(x,y)]
0
Uri Goren

En plus des réponses de @Martijn Pieters, je voudrais simplement ajouter le code suivant pour expliquer les opérations and et or en action.

and renvoie la première valeur falsy rencontrée, sinon le dernier argument évalué. 

De même, or renvoie la première valeur de vérité rencontrée, sinon le dernier argument évalué. 

nl1 = [3,3,3,3,0,0,0,0]
nl2 = [2,2,0,0,2,2,0,0]
nl3 = [1,0,1,0,1,0,1,0]
and_list = [a and b and c for a,b,c in Zip(nl1,nl2,nl3)]
or_list = [a or b or c for a,b,c in Zip(nl1,nl2,nl3)]

Les valeurs sont

and_list = [1, 0, 0, 0, 0, 0, 0, 0]

or_list = [3, 3, 3, 3, 2, 2, 1, 0]

0
Tony