web-dev-qa-db-fra.com

vérifier si tous les éléments d'une liste sont identiques

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:

  • parcourir la liste
  • comparer les éléments adjacents
  • et AND toutes les valeurs booléennes obtenues

Mais 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:

  • jusqu'à 20 fois plus performantes que des listes identiques
  • jusqu'à 2,5 fois plus performant sur les listes restreintes

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.

339
max

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:

  1. Dans checkEqual2 le contenu doit être hashable.
  2. 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.
  3. checkEqual1 s'arrête dès qu'une différence est trouvée.
  4. Comme checkEqual1 contient plus de code Python, il est moins efficace lorsque de nombreux éléments sont identiques au début.
  5. Étant donné que 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.
  6. Pour 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
367
kennytm

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
264
Ivo van der Wijk

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.

129
ninjagecko

Un travail de comparaison défini:

len(set(the_list)) == 1

L'utilisation de set supprime tous les éléments en double.

40
cbalawat

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.
24
codaddict

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.

  1. Courts-circuits: il cessera de consommer des éléments de l'itérable dès qu'il trouvera le premier élément non égal.
  2. Ne nécessite pas que les articles soient lavables.
  3. Il est paresseux et ne nécessite que O(1) mémoire supplémentaire pour effectuer la vérification.

1En d’autres termes, je ne peux pas prendre le crédit d’avoir proposé la solution, ni même l’avoir trouvé .

15
mgilson

Voici deux façons simples de le faire

en utilisant set ()

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

en utilisant all ()

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].

15
Christopher Nuccio

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
10
6502

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)
8
Jerub

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
4
Joshua Burns

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.

4
machineghost

Vérifiez si tous les éléments sont égaux au premier.

np.allclose(array, array[0])

4
Gusev Slava

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
3
DePP

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.

3
Marcus Lind

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.

3
Robert Rossney

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

2
Luis B

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:]))
2
SuperNova

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
2
U10-Forward
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".

2
itertool
>>> 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"
2
pyfunc
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)))
1
user3015260

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 .

1
Foad

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:
1
Lumo5

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é.)

1
ninjagecko

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
0
Saeed