Überprüfen Sie, ob alle Elemente in einer Liste identisch sind


389

Ich brauche folgende Funktion:

Eingabe : alist

Ausgabe :

  • True wenn alle Elemente in der Eingabeliste unter Verwendung des Standard-Gleichheitsoperators als gleich bewertet werden;
  • False Andernfalls.

Leistung : Natürlich ziehe ich es vor, keinen unnötigen Overhead zu verursachen.

Ich denke, es wäre am besten:

  • Durchlaufen Sie die Liste
  • Vergleichen Sie benachbarte Elemente
  • und ANDalle resultierenden Booleschen Werte

Aber ich bin mir nicht sicher, was der pythonischste Weg ist, das zu tun.


Das Fehlen einer Kurzschlussfunktion schmerzt nur bei einem langen Eingang (über ~ 50 Elemente), der frühzeitig ungleiche Elemente aufweist. Wenn dies häufig genug auftritt (wie oft hängt davon ab, wie lang die Listen sein können), ist der Kurzschluss erforderlich. Der beste Kurzschlussalgorithmus scheint @KennyTM zu sein checkEqual1. Dies zahlt jedoch erhebliche Kosten:

  • Bis zu 20x in nahezu identischen Leistungslisten
  • Bis zu 2,5-fache Leistung auf Shortlists

Wenn die langen Eingänge mit frühen ungleichen Elementen nicht (oder nur selten genug) auftreten, ist kein Kurzschluss erforderlich. Dann ist die @ Ivo van der Wijk-Lösung bei weitem die schnellste.


3
Gleich wie in a == boder identisch wie in a is b?
Kennytm

1
Sollte die Lösung leere Listen verarbeiten? Wenn ja, was sollte zurückgegeben werden?
Doug

1
Gleich wie in a == b. Sollte mit einer leeren Liste umgehen und True zurückgeben.
Max

2
Obwohl ich weiß, dass es langsamer ist als einige der anderen Empfehlungen, bin ich überrascht, dass functools.reduce(operator.eq, a)es nicht vorgeschlagen wurde.
user2846495

Antworten:


420

Allgemeine Methode:

def checkEqual1(iterator):
    iterator = iter(iterator)
    try:
        first = next(iterator)
    except StopIteration:
        return True
    return all(first == rest for rest in iterator)

Einzeiler:

def checkEqual2(iterator):
   return len(set(iterator)) <= 1

Auch einzeilig:

def checkEqual3(lst):
   return lst[1:] == lst[:-1]

Der Unterschied zwischen den 3 Versionen besteht darin, dass:

  1. In checkEqual2dem Inhalt muss hashable sein.
  2. checkEqual1 und checkEqual2 kann beliebige Iteratoren verwenden, checkEqual3muss jedoch eine Sequenzeingabe vornehmen, normalerweise konkrete Container wie eine Liste oder ein Tupel.
  3. checkEqual1 stoppt, sobald ein Unterschied festgestellt wird.
  4. Schon seit checkEqual1 mehr Python-Code enthält, ist es weniger effizient, wenn viele der Elemente am Anfang gleich sind.
  5. Seit checkEqual2undcheckEqual3 immer O (N) Kopiervorgänge durchführen, werden sie länger dauern , wenn die meisten Ihrer Eingabe falsch zurück.
  6. Für checkEqual2und checkEqual3es ist schwieriger, den Vergleich von a == bbis anzupassen a is b.

timeit Ergebnis für Python 2.7 und (nur s1, s4, s7, s9 sollten True zurückgeben)

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 = []

wir bekommen

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

Hinweis:

# 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

1
Vielen Dank, dies ist eine wirklich hilfreiche Erklärung für Alternativen. Können Sie bitte Ihre Leistungstabelle überprüfen - ist alles in ms und sind die Zahlen in den richtigen Zellen?
Max

7
@max: Ja. Beachten Sie, dass 1 ms = 1000 usec.
Kennytm

1
Nicht Speichernutzungsanalyse für sehr großen Arrays, eine native Lösung vergessen , die weg optimiert ruft , obj.__eq__wenn lhs is rhs, und Out-of-Order - Optimierungen zu ermöglichen Kurzschlüsse Listen schneller sortierten.
Glenn Maynard

3
Ivo van der Wijk hat eine bessere Lösung für Sequenzen, die etwa fünfmal schneller sind als set und O (1) im Speicher.
Aaronasterling

2
Es gibt auch ein itertoolsRezept, das ich als Antwort hinzugefügt habe. Es könnte sich lohnen, das in deine Timing-Matrix zu werfen :-).
mgilson

298

Eine Lösung, die schneller ist als die Verwendung von set (), die mit Sequenzen (nicht iterablen) funktioniert, besteht darin, einfach das erste Element zu zählen. Dies setzt voraus, dass die Liste nicht leer ist (aber das ist trivial zu überprüfen und selbst zu entscheiden, was das Ergebnis auf einer leeren Liste sein soll)

x.count(x[0]) == len(x)

einige einfache Benchmarks:

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

5
OMG, das ist 6 mal schneller als die eingestellte Lösung! (280 Millionen Elemente / Sek. Gegenüber 45 Millionen Elementen / Sek. Auf meinem Laptop). Warum??? Und gibt es eine Möglichkeit, es so zu modifizieren, dass es kurzschließt (ich denke nicht ...)
max

2
Ich denke, list.count hat eine hochoptimierte C-Implementierung und die Länge der Liste wird intern gespeichert, so dass len () auch billig ist. Es gibt keine Möglichkeit, count () kurzzuschließen, da Sie wirklich alle Elemente überprüfen müssen, um die richtige Anzahl zu erhalten.
Ivo van der Wijk

Kann ich es ändern in: x.count(next(x)) == len(x)damit es für jeden Container x funktioniert? Ahh .. nm, habe gerade gesehen, dass .count nur für Sequenzen verfügbar ist .. Warum ist es nicht für andere eingebaute Container implementiert? Ist das Zählen in einem Wörterbuch von Natur aus weniger sinnvoll als in einer Liste?
Max

4
Ein Iterator hat möglicherweise keine Länge. ZB kann es unendlich sein oder nur dynamisch erzeugt werden. Sie können seine Länge nur finden, indem Sie ihn in eine Liste konvertieren, die die meisten Vorteile der Iteratoren zunichte macht
Ivo van der Wijk,

Entschuldigung, ich meinte, warum countnicht für iterables implementiert ist, nicht warum lennicht für Iteratoren verfügbar. Die Antwort ist wahrscheinlich, dass es nur ein Versehen ist. Für uns ist dies jedoch irrelevant, da die Standardeinstellung .count()für Sequenzen sehr langsam ist (reines Python). Der Grund , warum Ihre Lösung so schnell ist , ist , dass es auf dem C-implementiertes beruht , countbereitgestellt durch list. Ich nehme also an, dass jedes iterierbare countVerfahren zur Implementierung der Methode in C von Ihrem Ansatz profitiert.
Max

164

Der einfachste und eleganteste Weg ist wie folgt:

all(x==myList[0] for x in myList)

(Ja, dies funktioniert sogar mit der leeren Liste! Dies liegt daran, dass dies einer der wenigen Fälle ist, in denen Python eine faule Semantik hat.)

In Bezug auf die Leistung wird dies zum frühestmöglichen Zeitpunkt fehlschlagen, so dass es asymptotisch optimal ist.


Dies funktioniert, ist aber etwas (1,5x) langsamer als @KennyTM checkEqual1. Ich bin mir nicht sicher warum.
Max

4
max: Wahrscheinlich, weil ich mich nicht darum first=myList[0] all(x==first for x in myList)
gekümmert habe

Ich denke, dass myList [0] bei jeder Iteration ausgewertet wird. >>> timeit.timeit ('all ([y == x [0] für y in x])', 'x = [1] * 4000', number = 10000) 2.707076672740641 >>> timeit.timeit ('x0 = x [0]; all ([y == x0 für y in x]) ',' x = [1] * 4000 ', Nummer = 10000) 2.0908854261426484
Matt Liberty

1
Ich sollte natürlich klarstellen, dass die Optimierung first=myList[0]eine IndexErrorauf eine leere Liste wirft , sodass Kommentatoren, die über diese von mir erwähnte Optimierung gesprochen haben, sich mit dem Randfall einer leeren Liste befassen müssen. Das Original ist jedoch in Ordnung ( x==myList[0]ist in Ordnung, allda es niemals ausgewertet wird, wenn die Liste leer ist).
Ninjagecko

1
Dies ist eindeutig der richtige Weg dorthin. Wenn Sie in jedem Fall Geschwindigkeit wollen, verwenden Sie so etwas wie Numpy.
Henry Gomersall

45

Eine festgelegte Vergleichsarbeit:

len(set(the_list)) == 1

Mit werden setalle doppelten Elemente entfernt.


26

Sie können die Liste in einen Satz konvertieren. Ein Satz darf keine Duplikate enthalten. Wenn also alle Elemente in der ursprünglichen Liste identisch sind, enthält die Menge nur ein Element.

if len(sets.Set(input_list)) == 1
// input_list has all identical elements.

Das ist schön, aber es schließt nicht kurz und Sie müssen die Länge der resultierenden Liste berechnen.
Aaronasterling

15
warum nicht einfach len(set(input_list)) == 1?
Nick Dandoulakis

2
@codaddict. Dies bedeutet, dass selbst wenn die ersten beiden Elemente unterschiedlich sind, die gesamte Suche abgeschlossen wird. Außerdem wird O (k) zusätzlicher Platz verwendet, wobei k die Anzahl der unterschiedlichen Elemente in der Liste ist.
Aaronasterling

1
@max. weil das Erstellen des Sets in C erfolgt und Sie eine schlechte Implementierung haben. Sie sollten dies zumindest in einem Generatorausdruck tun. In der Antwort von KennyTM erfahren Sie, wie Sie dies ohne Verwendung eines Sets richtig machen.
Aaronasterling

1
sets.Set ist "Veraltet seit Version 2.6: Die eingebauten set / frozenset-Typen ersetzen dieses Modul." (von docs.python.org/2/library/sets.html )
Moberg

21

Für das, was es wert ist, wurde dies kürzlich auf die Python-Ideen-Mailingliste gesetzt . Es stellt sich heraus, dass es dafür bereits ein itertools-Rezept gibt : 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)

Angeblich funktioniert es sehr gut und hat ein paar schöne Eigenschaften.

  1. Kurzschlüsse: Der Verbrauch von Elementen aus dem iterierbaren Element wird beendet, sobald das erste ungleiche Element gefunden wird.
  2. Es ist nicht erforderlich, dass Elemente hashbar sind.
  3. Es ist faul und benötigt nur O (1) zusätzlichen Speicher, um die Prüfung durchzuführen.

1 Mit anderen Worten, ich kann weder die Anerkennung für die Entwicklung der Lösung noch die Anerkennung dafür finden, dass ich sie überhaupt gefunden habe.


3
Viel schneller als die schnellste Antwort, die hier im schlimmsten Fall aufgeführt ist.
ChaimG

return next(g, f := next(g, g)) == f(ab py3.8 natürlich)
Chris_Rands

17

Hier sind zwei einfache Möglichkeiten, dies zu tun

mit set ()

Beim Konvertieren der Liste in einen Satz werden doppelte Elemente entfernt. Wenn also die Länge der konvertierten Menge 1 beträgt, bedeutet dies, dass alle Elemente gleich sind.

len(set(input_list))==1

Hier ist ein Beispiel

>>> a = ['not', 'the', 'same']
>>> b = ['same', 'same', 'same']
>>> len(set(a))==1  # == 3
False
>>> len(set(b))==1  # == 1
True

mit all ()

Dadurch wird das erste Element der Eingabeliste mit jedem anderen Element in der Liste verglichen (Äquivalenz). Wenn alle gleichwertig sind, wird True zurückgegeben, andernfalls wird False zurückgegeben.

all(element==input_list[0] for element in input_list)

Hier ist ein Beispiel

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

PS Wenn Sie überprüfen, ob die gesamte Liste einem bestimmten Wert entspricht, können Sie den Wert für input_list [0] eingeben.


1
Für Personen, die an Laufzeit interessiert sind, len(set(a))dauerte die Ausführung einer Liste mit 10.000.000 Elementen 0,09 s, während die Ausführung all0,9 s (zehnmal länger) dauerte.
Elliptica

2
Ich mag diese Antwort auch wegen ihrer pythonischen Einfachheit, zusätzlich zu der von @Elliptica
NickBraunagel

11

Dies ist eine weitere Option, schneller als len(set(x))==1bei langen Listen (verwendet Kurzschluss)

def constantList(x):
    return x and [x[0]]*len(x) == x

Es ist dreimal langsamer als die auf meinem Computer eingestellte Lösung, wobei Kurzschlüsse ignoriert werden. Wenn das ungleiche Element im Durchschnitt im ersten Drittel der Liste gefunden wird, ist es im Durchschnitt schneller.
Max

9

Dies ist eine einfache Methode:

result = mylist and all(mylist[0] == elem for elem in mylist)

Dies ist etwas komplizierter, es entsteht ein Funktionsaufruf-Overhead, aber die Semantik ist klarer formuliert:

def all_identical(seq):
    if not seq:
        # empty list is False.
        return False
    first = seq[0]
    return all(first == elem for elem in seq)

Sie können hier einen redundanten Vergleich vermeiden, indem Sie verwenden for elem in mylist[1:]. Zweifelsohne verbessert es die Geschwindigkeit erheblich, da ich denke, elem[0] is elem[0]dass der Dolmetscher diesen Vergleich wahrscheinlich sehr schnell durchführen kann.
Brendan

5

Überprüfen Sie, ob alle Elemente dem ersten entsprechen.

np.allclose(array, array[0])


Benötigt ein Modul eines Drittanbieters.
Bachsau

4

Zweifel, dies ist die "pythonischste", aber so etwas wie:

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

würde den Trick machen.


1
Ihre forSchleife kann if any(item != list[0] for item in list[1:]): return Falsemit genau der gleichen Semantik pythonischer gestaltet werden .
Musiphil

4

Wenn Sie an etwas Lesbarem interessiert sind (aber natürlich nicht so effizient), können Sie Folgendes versuchen:

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

Ich versuche tatsächlich zu sehen, ob alle Elemente in einer Liste identisch sind. nicht, wenn zwei separate Listen identisch sind.
Max

4

Konvertieren Sie die Liste in die Menge und suchen Sie dann die Anzahl der Elemente in der Menge. Wenn das Ergebnis 1 ist, hat es identische Elemente, und wenn nicht, sind die Elemente in der Liste nicht identisch.

list1 = [1,1,1]
len(set(list1)) 
>1

list1 = [1,2,3]
len(set(list1)
>3

4

In Bezug auf die Verwendung reduce()mit lambda. Hier ist ein Arbeitscode, den ich persönlich für viel schöner halte als einige der anderen Antworten.

reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))

Gibt ein Tupel zurück, bei dem der erste Wert der Boolesche Wert ist, wenn alle Elemente gleich sind oder nicht.


Es gibt einen kleinen Fehler im Code, wie er geschrieben wurde (Versuch [1, 2, 2]): Der vorherige boolesche Wert wird nicht berücksichtigt. Dies kann durch Ersetzen fixiert werden x[1] == ymit x[0] and x[1] == y.
Schot

3

Ja, würde ich:

not any((x[i] != x[i+1] for i in range(0, len(x)-1)))

as anyhört auf, das iterable zu suchen, sobald es eine TrueBedingung findet.


Sie benötigen keine zusätzlichen Klammern um den Generatorausdruck, wenn dies das einzige Argument ist.
Ninjagecko

so ist all(), verwenden warum nicht all(x == seq[0] for x in seq)? sieht pythonischer aus und sollte das gleiche tun
Chen A.

2
>>> 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
def allTheSame(i):
    j = itertools.groupby(i)
    for k in j: break
    for k in j: return False
    return True

Funktioniert in Python 2.4, das nicht "alle" enthält.


1
for k in j: breakist äquivalent zu next(j). Sie hätten es auch tun können, def allTheSame(x): return len(list(itertools.groupby(x))<2)wenn Sie sich nicht um Effizienz gekümmert hätten.
Ninjagecko

2

Kann Karte und Lambda verwenden

lst = [1,1,1,1,1,1,1,1,1]

print all(map(lambda x: x == lst[0], lst[1:]))

2

Oder verwenden Sie die diffMethode der Numpy:

import numpy as np
def allthesame(l):
    return np.all(np.diff(l)==0)

Und um anzurufen:

print(allthesame([1,1,1]))

Ausgabe:

True

Ich denke not np.any(np.diff(l))könnte ein bisschen schneller sein.
GZ0

2

Oder verwenden Sie die diff-Methode von numpy:

import numpy as np
def allthesame(l):
    return np.unique(l).shape[0]<=1

Und um anzurufen:

print(allthesame([1,1,1]))

Ausgabe:

Wahr


Diese Antwort ist identisch mit einer Antwort von U9-Forward aus dem letzten Jahr.
Mhwombat

Gutes Auge! Ich habe dieselbe Struktur / API verwendet, aber meine Methode verwendet np.unique und shape. Die Funktion von U9 verwendet np.all () und np.diff () - ich verwende keine dieser Funktionen.
Luis B

1

Du kannst tun:

reduce(and_, (x==yourList[0] for x in yourList), True)

Es ist ziemlich ärgerlich, dass Sie mit Python die Operatoren wie importieren operator.and_. Ab Python3 müssen Sie auch importieren functools.reduce.

(Sie sollten diese Methode nicht verwenden, da sie nicht unterbrochen wird, wenn ungleiche Werte gefunden werden, sondern die gesamte Liste weiter untersucht. Sie wird hier nur als Antwort auf Vollständigkeit aufgeführt.)


Dies würde keinen Kurzschluss verursachen. Warum würden Sie es Ihrer anderen Lösung vorziehen?
Max

@max: genau aus diesem grund würdest du nicht; Der Vollständigkeit halber habe ich es aufgenommen. Ich sollte es wahrscheinlich bearbeiten, um das zu erwähnen, danke.
Ninjagecko

1
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]

Der nächste wird kurzschließen:

all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))

Ihr erster Code war offensichtlich falsch: reduce(lambda a,b:a==b, [2,2,2])ergibt False... Ich habe ihn bearbeitet, aber auf diese Weise ist er nicht mehr schön
berdario

@berdario Dann hättest du deine eigene Antwort schreiben sollen, anstatt zu ändern, was jemand anderes geschrieben hat. Wenn Sie der Meinung sind, dass diese Antwort falsch war, können Sie sie kommentieren und / oder ablehnen.
Gorpik

3
Es ist besser, etwas falsch zu beheben, als es allen Leuten zum Lesen zu überlassen und möglicherweise die Kommentare zu verpassen, die erklären, warum das falsch war
berdario

3
"Wann soll ich Beiträge bearbeiten?" "Jedes Mal, wenn Sie das Gefühl haben, dass Sie den Beitrag verbessern können, sind Sie dazu geneigt. Das Bearbeiten wird empfohlen!"
Berdario

1

Ändern Sie die Liste in einen Satz. Wenn die Größe des Sets nur 1 beträgt, müssen sie gleich gewesen sein.

if len(set(my_list)) == 1:

1

Es gibt auch eine reine rekursive Python-Option:

 def checkEqual(lst):
    if len(lst)==2 :
        return lst[0]==lst[1]
    else:
        return lst[0]==lst[1] and checkEqual(lst[1:])

Aus irgendeinem Grund ist es jedoch in einigen Fällen zwei Größenordnungen langsamer als andere Optionen. Ich komme aus der C-Sprachmentalität und habe erwartet, dass dies schneller geht, aber das ist es nicht!

Der andere Nachteil ist, dass es in Python eine Rekursionsgrenze gibt, die in diesem Fall angepasst werden muss. Zum Beispiel mit diesem .


0

Sie können verwenden .nunique(), um die Anzahl der eindeutigen Elemente in einer Liste zu finden.

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

Sie können verwenden set. Es wird ein Satz erstellt und sich wiederholende Elemente entfernt. Überprüfen Sie dann, ob es nicht mehr als 1 Element enthält.

if len(set(your_list)) <= 1:
    print('all ements are equal')

Beispiel:

>>> len(set([5, 5])) <= 1
True
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.