J'ai besoin de la fonction suivante:
Entrée: a list
Sortie:
True
si tous les éléments de la liste d'entrée sont égaux, en utilisant l'opérateur d'égalité standard;False
sinon.Performance: Bien sûr, je préfère ne pas encourir de frais généraux inutiles.
Je pense qu'il serait préférable de:
AND
toutes les valeurs booléennes obtenuesMais je ne sais pas quelle est la façon la plus pythonique de le faire.
EDIT:
Merci pour toutes les bonnes réponses. J'en ai évalué plusieurs et il était très difficile de choisir entre les solutions @KennyTM et @Ivo van der Wijk.
L'absence de fonction de court-circuit ne concerne que les entrées longues (plus de 50 éléments) contenant des éléments inégaux à un stade précoce. Si cela se produit assez souvent (la fréquence dépend de la longueur des listes), le court-circuit est nécessaire. Le meilleur algorithme de court-circuit semble être @KennyTM checkEqual1
. Cela coûte cependant un coût important pour cela:
Si les entrées longues avec des éléments inégaux précoces ne se produisent pas (ou se produisent assez rarement), un court-circuit n'est pas nécessaire. Puis, de loin, le plus rapide est la solution @Ivo van der Wijk.
Méthode générale:
def checkEqual1(iterator):
iterator = iter(iterator)
try:
first = next(iterator)
except StopIteration:
return True
return all(first == rest for rest in iterator)
Bon mot:
def checkEqual2(iterator):
return len(set(iterator)) <= 1
Également une doublure:
def checkEqual3(lst):
return lst[1:] == lst[:-1]
La différence entre les 3 versions sont les suivantes:
checkEqual2
le contenu doit être hashable.checkEqual1
et checkEqual2
peuvent utiliser n'importe quel itérateur, mais checkEqual3
doit utiliser une entrée de séquence, généralement des conteneurs concrets, tels qu'une liste ou un Tuple.checkEqual1
s'arrête dès qu'une différence est trouvée.checkEqual1
contient plus de code Python, il est moins efficace lorsque de nombreux éléments sont identiques au début.checkEqual2
et checkEqual3
effectuent toujours les opérations de copie O(N), elles prendront plus de temps si la plupart de vos entrées renvoient la valeur False.checkEqual2
et checkEqual3
, il est plus difficile d'adapter la comparaison de a == b
à a is b
.timeit
résultat, pour Python 2.7 et (seuls s1, s4, s7, s9 doivent renvoyer la valeur True)
s1 = [1] * 5000
s2 = [1] * 4999 + [2]
s3 = [2] + [1]*4999
s4 = [set([9])] * 5000
s5 = [set([9])] * 4999 + [set([10])]
s6 = [set([10])] + [set([9])] * 4999
s7 = [1,1]
s8 = [1,2]
s9 = []
on a
| checkEqual1 | checkEqual2 | checkEqual3 | checkEqualIvo | checkEqual6502 |
|-----|-------------|-------------|--------------|---------------|----------------|
| s1 | 1.19 msec | 348 usec | 183 usec | 51.6 usec | 121 usec |
| s2 | 1.17 msec | 376 usec | 185 usec | 50.9 usec | 118 usec |
| s3 | 4.17 usec | 348 usec | 120 usec | 264 usec | 61.3 usec |
| | | | | | |
| s4 | 1.73 msec | | 182 usec | 50.5 usec | 121 usec |
| s5 | 1.71 msec | | 181 usec | 50.6 usec | 125 usec |
| s6 | 4.29 usec | | 122 usec | 423 usec | 61.1 usec |
| | | | | | |
| s7 | 3.1 usec | 1.4 usec | 1.24 usec | 0.932 usec | 1.92 usec |
| s8 | 4.07 usec | 1.54 usec | 1.28 usec | 0.997 usec | 1.79 usec |
| s9 | 5.91 usec | 1.25 usec | 0.749 usec | 0.407 usec | 0.386 usec |
Remarque:
# http://stackoverflow.com/q/3844948/
def checkEqualIvo(lst):
return not lst or lst.count(lst[0]) == len(lst)
# http://stackoverflow.com/q/3844931/
def checkEqual6502(lst):
return not lst or [lst[0]]*len(lst) == lst
Une solution plus rapide que d’utiliser set () qui fonctionne sur les séquences (pas les itérables) consiste à compter le premier élément. Cela suppose que la liste n'est pas vide (mais c'est facile à vérifier, et décidez vous-même quel devrait être le résultat sur une liste vide)
x.count(x[0]) == len(x)
quelques repères simples:
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*5000', number=10000)
1.4383411407470703
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*4999+[2]', number=10000)
1.4765670299530029
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*5000', number=10000)
0.26274609565734863
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*4999+[2]', number=10000)
0.25654196739196777
La manière la plus simple et la plus élégante est la suivante:
all(x==myList[0] for x in myList)
(Oui, cela fonctionne même avec la liste vide! C'est parce que c'est l'un des rares cas où python a une sémantique paresseuse.)
En ce qui concerne les performances, cela échouera le plus tôt possible et sera donc asymptotiquement optimal.
Un travail de comparaison défini:
len(set(the_list)) == 1
L'utilisation de set
supprime tous les éléments en double.
Vous pouvez convertir la liste en un ensemble. Un ensemble ne peut pas avoir de doublons. Donc, si tous les éléments de la liste d'origine sont identiques, l'ensemble n'aura qu'un seul élément.
if len(sets.Set(input_list)) == 1
// input_list has all identical elements.
Pour ce que cela vaut, cela est apparu récemment sur la liste de diffusion python-ideas . Il s'avère qu'il existe un recette d'itertools pour le faire déjà:1
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
Soi-disant, il fonctionne très bien et a quelques belles propriétés.
1En d’autres termes, je ne peux pas prendre le crédit d’avoir proposé la solution, ni même l’avoir trouvé .
Lors de la conversion de la liste en un ensemble, les éléments en double sont supprimés. Donc, si la longueur de l'ensemble converti est 1, cela implique que tous les éléments sont identiques.
len(set(input_list))==1
Voici un exemple
>>> a = ['not', 'the', 'same']
>>> b = ['same', 'same', 'same']
>>> len(set(a))==1 # == 3
False
>>> len(set(b))==1 # == 1
True
Ceci comparera (équivalence) le premier élément de la liste d'entrée à tous les autres éléments de la liste. Si tous sont équivalents, True sera retourné, sinon False sera retourné.
all(element==input_list[0] for element in input_list)
Voici un exemple
>>> a = [1, 2, 3, 4, 5]
>>> b = [1, 1, 1, 1, 1]
>>> all(number==a[0] for number in a)
False
>>> all(number==b[0] for number in b)
True
P.S Si vous vérifiez si la liste entière est équivalente à une certaine valeur, vous pouvez remplacer la valeur dans input_list [0].
C'est une autre option, plus rapide que len(set(x))==1
pour les longues listes (utilise un court-circuit)
def constantList(x):
return x and [x[0]]*len(x) == x
C'est un moyen simple de le faire:
result = mylist and all(mylist[0] == elem for elem in mylist)
C’est un peu plus compliqué, cela entraîne une surcharge d’appel de fonction, mais la sémantique est plus clairement définie:
def all_identical(seq):
if not seq:
# empty list is False.
return False
first = seq[0]
return all(first == elem for elem in seq)
Si vous êtes intéressé par quelque chose d'un peu plus lisible (mais évidemment pas aussi efficace), vous pouvez essayer:
def compare_lists(list1, list2):
if len(list1) != len(list2): # Weed out unequal length lists.
return False
for item in list1:
if item not in list2:
return False
return True
a_list_1 = ['Apple', 'orange', 'grape', 'pear']
a_list_2 = ['pear', 'orange', 'grape', 'Apple']
b_list_1 = ['Apple', 'orange', 'grape', 'pear']
b_list_2 = ['Apple', 'orange', 'banana', 'pear']
c_list_1 = ['Apple', 'orange', 'grape']
c_list_2 = ['grape', 'orange']
print compare_lists(a_list_1, a_list_2) # Returns True
print compare_lists(b_list_1, b_list_2) # Returns False
print compare_lists(c_list_1, c_list_2) # Returns False
Doute que ce soit le "plus pythonique", mais quelque chose comme:
>>> falseList = [1,2,3,4]
>>> trueList = [1, 1, 1]
>>>
>>> def testList(list):
... for item in list[1:]:
... if item != list[0]:
... return False
... return True
...
>>> testList(falseList)
False
>>> testList(trueList)
True
ferait l'affaire.
Vérifiez si tous les éléments sont égaux au premier.
np.allclose(array, array[0])
Convertissez la liste en un ensemble, puis recherchez le nombre d'éléments dans cet ensemble. Si le résultat est 1, il a des éléments identiques et sinon, les éléments de la liste ne sont pas identiques.
list1 = [1,1,1]
len(set(list1))
>1
list1 = [1,2,3]
len(set(list1)
>3
En ce qui concerne l'utilisation de reduce()
avec lambda
. Voici un code qui, à mon avis, est bien meilleur que certaines des autres réponses.
reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))
Retourne un triplet où la première valeur est le booléen si tous les éléments sont identiques ou non.
Je ferais:
not any((x[i] != x[i+1] for i in range(0, len(x)-1)))
as any
arrête la recherche de l'itérable dès qu'il trouve une condition True
.
Ou utilisez la méthode diff de numpy:
import numpy as np
def allthesame(l):
return np.unique(l).shape[0]<=1
Et appeler:
print(allthesame([1,1,1]))
Sortie:
Vrai
Peut utiliser map et lambda
lst = [1,1,1,1,1,1,1,1,1]
print all(map(lambda x: x == lst[0], lst[1:]))
Ou utilisez la méthode diff
de numpy:
import numpy as np
def allthesame(l):
return np.all(np.diff(l)==0)
Et appeler:
print(allthesame([1,1,1]))
Sortie:
True
def allTheSame(i):
j = itertools.groupby(i)
for k in j: break
for k in j: return False
return True
Fonctionne dans Python 2.4, qui n'a pas "tous".
>>> a = [1, 2, 3, 4, 5, 6]
>>> z = [(a[x], a[x+1]) for x in range(0, len(a)-1)]
>>> z
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
# Replacing it with the test
>>> z = [(a[x] == a[x+1]) for x in range(0, len(a)-1)]
>>> z
[False, False, False, False, False]
>>> if False in z : Print "All elements are not equal"
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]
Le prochain court-circuitera:
all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))
Il existe également une option pure Python récursive:
def checkEqual(lst):
if len(lst)==2 :
return lst[0]==lst[1]
else:
return lst[0]==lst[1] and checkEqual(lst[1:])
Cependant, pour une raison quelconque, il est parfois deux fois plus lent que les autres options. Venant de la mentalité du langage C, je m'attendais à ce que ce soit plus rapide, mais ce n'est pas le cas!
L’autre inconvénient est qu’il existe une limite de récursivité dans Python qui doit être ajustée dans ce cas. Par exemple, en utilisant this .
Changer la liste en un ensemble. Ensuite, si la taille de l'ensemble est seulement 1, ils doivent avoir été les mêmes.
if len(set(my_list)) == 1:
Tu peux faire:
reduce(and_, (x==yourList[0] for x in yourList), True)
Il est assez agaçant que python vous oblige à importer des opérateurs tels que operator.and_
. A partir de python3, vous devrez également importer functools.reduce
.
(Vous ne devez pas utiliser cette méthode, car elle ne cassera pas si elle trouve des valeurs non égales, mais continuera à examiner toute la liste. Elle est simplement incluse ici en tant que réponse à la question de l'exhaustivité.)
Vous pouvez utiliser .nunique()
pour rechercher le nombre d'éléments uniques dans une liste.
def identical_elements(list):
series = pd.Series(list)
if series.nunique() == 1: identical = True
else: identical = False
return identical
identical_elements(['a', 'a'])
Out[427]: True
identical_elements(['a', 'b'])
Out[428]: False