Listedeki tüm öğelerin aynı olup olmadığını kontrol edin


389

Aşağıdaki fonksiyona ihtiyacım var:

Giriş : alist

Çıktı :

  • True giriş listesindeki tüm elemanlar standart eşitlik operatörünü kullanarak birbirine eşit olarak değerlendirilirse;
  • False aksi takdirde.

Performans : elbette, gereksiz ek yüke maruz kalmamayı tercih ederim.

Ben en iyisi olacağını hissediyorum:

  • liste boyunca yineleme
  • bitişik elemanları karşılaştır
  • ve ANDsonuçta elde edilen tüm Boolean değerleri

Ama bunu yapmanın en Pitonik yolunun ne olduğundan emin değilim.


Kısa devre özelliğinin olmaması, yalnızca eşit olmayan öğelere sahip uzun bir girişe (~ 50'den fazla eleman) zarar verir. Bu yeterince sık meydana gelirse (listelerin ne kadar uzunluğuna bağlı olabilir), kısa devre gereklidir. En iyi kısa devre algoritması @KennyTM gibi görünüyor checkEqual1. Bununla birlikte, bunun için önemli bir maliyet ödüyor:

  • 20x'e kadar performansta neredeyse aynı listeler
  • Kısa listelerde 2,5 kata kadar performans

Erken eşit olmayan öğelere sahip uzun girişler olmazsa (veya nadiren yeterli değilse), kısa devre gerekmez. Sonra, en hızlısı @Ivo van der Wijk çözümüdür.


3
Olarak eşit a == bveya özdeş a is b?
kennytm

1
Çözüm boş listeleri işlemeli mi? Eğer öyleyse, ne iade edilmelidir?
Doug

1
A == b ile aynıdır. Boş listeyi işlemeli ve True döndürmelidir.
en fazla

2
Diğer önerilerin bazılarından daha yavaş olduğunu bilsem de, önerilmediğine şaşırdım functools.reduce(operator.eq, a).
user2846495

Yanıtlar:


420

Genel yöntem:

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

Tek astarı:

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

Ayrıca bir astar:

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

3 versiyon arasındaki fark şudur:

  1. Gelen checkEqual2içerik hashable olmalıdır.
  2. checkEqual1ve checkEqual2herhangi bir yineleyici kullanabilir, ancak checkEqual3bir dizi girişi, tipik olarak bir liste veya demet gibi beton kaplar almalıdır.
  3. checkEqual1 fark bulunur bulunmaz durur.
  4. Yana checkEqual1daha Python kodunu içeren maddelerin birçoğu başlangıçta eşit olduğunda, daha az verimlidir.
  5. Yana checkEqual2ve checkEqual3her zaman O (K) kopyalama işlemleri gerçekleştirmek girişiniz en Yanlış dönecektir, onlar daha uzun sürer.
  6. İçin checkEqual2ve checkEqual3ondan karşılaştırma adapte zordur a == biçin a is b.

timeit sonuç, Python 2.7 ve (yalnızca s1, s4, s7, s9 True döndürmelidir)

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

aldık

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

Not:

# 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
Teşekkürler, bu alternatiflerin gerçekten yararlı bir açıklaması. Lütfen performans tablonuzu iki kez kontrol edebilir misiniz - hepsi msec olarak mı ve doğru hücrelerdeki rakamlar mı?
en fazla

7
@max: Evet. 1 msn = 1000 usec olduğunu unutmayın.
kennytm

1
Çok büyük diziler için bellek kullanım analizini, çağrıları obj.__eq__ne zaman uzağa optimize eden yerel bir çözümü lhs is rhsve sıralı listelerin daha hızlı bir şekilde kısa devre yapılmasına olanak tanıyan sıra dışı optimizasyonları unutmayın.
Glenn Maynard

3
Ivo van der Wijk, dizilerden yaklaşık 5 kat daha hızlı ve bellekte O (1) diziler için daha iyi bir çözüme sahiptir.
aaronasterling

2
itertoolsCevap olarak eklediğim bir tarif de var . Bunu zamanlama matrisinize atmaya değebilir :-).
mgilson

298

Diziler (yinelemesiz) üzerinde çalışan set () yöntemini kullanmaktan daha hızlı bir çözüm, yalnızca ilk öğeyi saymaktır. Bu, listenin boş olmadığını varsayar (ancak boş bir listede sonucun ne olması gerektiğine kendiniz karar vermeniz önemsizdir)

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

bazı basit kriterler:

>>> 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, bu ayarlanan çözümden 6 kat daha hızlı! (Dizüstü bilgisayarımda 280 milyon öğe / sn vs 45 milyon öğe / sn). Neden??? Ve kısa devreler (sanırım değil ...) böylece değiştirmek için herhangi bir yolu var mı?
max

2
List.count'un son derece optimize edilmiş bir C uygulaması var ve listenin uzunluğu dahili olarak saklanıyor, bu yüzden len () de ucuz. Doğru sayımı elde etmek için tüm öğeleri gerçekten kontrol etmeniz gerekeceğinden, kısa devre sayımının () bir yolu yoktur.
Ivo van der Wijk

Bunu x.count(next(x)) == len(x)herhangi bir kapsayıcı x için çalışacak şekilde değiştirebilir miyim ? Ahh .. nm, gördüm ki .count sadece diziler için kullanılabilir .. Neden diğer yerleşik konteynerler için uygulanmıyor? Bir sözlüğün içinde saymak, bir liste içinde olduğundan daha az anlamlı mıdır?
max

4
Bir yineleyicinin uzunluğu olmayabilir. Örneğin, sonsuz olabilir veya sadece dinamik olarak oluşturulabilir. Uzunluğunu yalnızca yineleyici avantajlarının çoğunu alan bir listeye dönüştürerek bulabilirsiniz
Ivo van der Wijk

Üzgünüm, demek istediğim countyinelemelere neden uygulanmadı, neden lenyineleyiciler için mevcut değil. Cevap muhtemelen bunun sadece bir gözetim olmasıdır. Ama bu bizim için çirkin, çünkü diziler için varsayılan .count()çok yavaş (saf python). Çözümünüzün bu kadar hızlı olmasının nedeni, counttarafından sağlanan C-uygulamasına dayanmasıdır list. Diyelim ki C'deki countyöntemi uygulamak ne olursa olsun yaklaşımınızdan faydalanacaktır.
maksimum

164

En basit ve zarif yol aşağıdaki gibidir:

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

(Evet, bu boş liste ile bile çalışır! Bunun nedeni, python'un tembel anlambilimine sahip olduğu birkaç durumdan biridir.)

Performans ile ilgili olarak, bu mümkün olan en erken zamanda başarısız olacaktır, bu nedenle asimptotik olarak optimaldir.


Bu işe yarıyor, ancak @KennyTM'den biraz (1,5x) daha yavaş checkEqual1. Neden olduğundan emin değilim.
maksimum

4
max: first=myList[0] all(x==first for x in myList)Belki de optimizasyonu yapmak için uğraşmadım , belki de
ninjagecko

MyList [0] 'un her yineleme ile değerlendirildiğini düşünüyorum. >>> timeit.timeit ('tümü (x in y için [y == x [0]]') ',' x = [1] * 4000 ', sayı = 10000) 2.707076672740641 >>> timeit.timeit (' x0 = x [0]; tümü ([x içinde y için y == x0]) ',' x = [1] * 4000 ', sayı = 10000) 2.0908854261426484
Matt Liberty

1
Ben tabii optimizasyonu olduğunu açıklamak gerekir first=myList[0]bir atacağım IndexErrorboş bir listenin kenardan dava ile uğraşmak zorunda kalacak bahsettiği optimizasyonu hakkında konuşuyorduk Yorum yapanlar böylece, boş bir listede. Ancak orijinal gayet ( x==myList[0]içinde gayet allliste boşsa o değerlendirilir asla çünkü).
ninjagecko

1
Bu açıkça doğru bir yoldur. Her durumda hız istiyorsanız, numpy gibi bir şey kullanın.
Henry Gomersall

45

Bir dizi karşılaştırma çalışması:

len(set(the_list)) == 1

Kullanarak settüm yinelenen öğeler kaldırılır.


26

Listeyi bir kümeye dönüştürebilirsiniz. Bir kümenin kopyaları olamaz. Dolayısıyla, orijinal listedeki tüm öğeler aynı ise, kümede yalnızca bir öğe bulunur.

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

bu güzel ama kısa devre yapmıyor ve sonuç listesinin uzunluğunu hesaplamanız gerekiyor.
aaronasterling

15
neden sadece len(set(input_list)) == 1?
Nick Dandoulakis

2
@codaddict. Bu, ilk iki öğe farklı olsa bile, tüm aramayı tamamlayacağı anlamına gelir. ayrıca k (listedeki ayrı elemanların sayısı) O (k) fazladan alanı kullanır.
aaronasterling

1
@max. çünkü seti oluşturmak C'de olur ve kötü bir uygulamanız olur. En azından bir jeneratör ifadesinde yapmalısınız. Set kullanmadan nasıl doğru yapılacağı konusunda KennyTM'nin cevabına bakın.
aaronasterling

1
sets.Set "2.6 sürümünden beri kullanımdan kaldırıldı: Yerleşik set / frozenset türleri bu modülün yerini alıyor." (dan docs.python.org/2/library/sets.html )
Moberg

21

Değeri için, bu son zamanlarda python-fikirler posta listesinde geldi . Bunu zaten yapmak için bir itertools tarifi olduğu ortaya çıkıyor : 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)

Sözde çok güzel bir performans sergiliyor ve birkaç güzel özelliği var.

  1. Kısa devreler: İlk eşit olmayan öğeyi bulur bulmaz yinelenen öğeleri tüketmeyi durduracaktır.
  2. Öğelerin yıkanabilir olmasını gerektirmez.
  3. Tembeldir ve kontrolü yapmak için sadece O (1) ek bellek gerektirir.

1 Başka bir deyişle, ben çözüm ile geliyor için kredi alamaz - ne de olsa kredi notun bulma onu.


3
En kötü senaryoda burada listelenen en hızlı cevaptan çok daha hızlı .
ChaimG

return next(g, f := next(g, g)) == f(elbette py3.8'den)
Chris_Rands

17

İşte bunu yapmanın iki basit yolu

set () kullanarak

Listeyi bir kümeye dönüştürürken, yinelenen öğeler kaldırılır. Dönüştürülen kümenin uzunluğu 1 ise, bu tüm öğelerin aynı olduğunu gösterir.

len(set(input_list))==1

İşte bir örnek

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

tümünü kullanarak ()

Bu, giriş listesinin ilk öğesini listedeki diğer tüm öğelerle karşılaştırır (eşdeğerlik). Hepsi eşdeğerse True döndürülür, aksi takdirde False döndürülür.

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

İşte bir örnek

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

Not: Tüm listenin belirli bir değere eşit olup olmadığını kontrol ediyorsanız, input_list [0] için değeri saklayabilirsiniz.


1
Çalışma zamanıyla ilgilenen insanlar için len(set(a))10.000.000 eleman listesinde performans yapmak 0.09 s alırken, performans all0.9 s (10 kat daha uzun) sürdü.
Elliptica

2
@Elliptica
NickBraunagel

11

Bu, len(set(x))==1uzun listelerden daha hızlı bir başka seçenektir (kısa devre kullanır)

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

Kısa devreyi yok sayarak bilgisayarımdaki ayarlanan çözümden 3 kat daha yavaş. Dolayısıyla, eşit olmayan öğe listenin ilk üçte birinde ortalama olarak bulunursa, ortalama olarak daha hızlıdır.
en fazla

9

Bunu yapmanın basit bir yolu:

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

Bu biraz daha karmaşıktır, işlev çağrısı tepegözüne neden olur, ancak anlambilim daha açık bir şekilde belirtilir:

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

Burada gereksiz bir karşılaştırmayı önleyebilirsiniz. for elem in mylist[1:] . Şüpheliyim, sanırım çünkü elem[0] is elem[0]tercüman muhtemelen bu karşılaştırmayı çok hızlı bir şekilde yapabilir.
Brendan

5

Tüm öğelerin birinciye eşit olup olmadığını kontrol edin.

np.allclose(array, array[0])


Üçüncü taraf modül gerekiyor.
Bachsau

4

Şüphe bu "en Pythonic", ama gibi bir şey:

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

hile yapardı.


1
Kişisel fordöngü fazla Pythonic içine yapılabilir if any(item != list[0] for item in list[1:]): return Falsetam olarak aynı anlamda.
musiphil

4

Biraz daha okunabilir bir şeyle ilgileniyorsanız (ancak elbette o kadar verimli değil) deneyebilirsiniz:

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

Aslında bir listedeki tüm elemanların aynı olup olmadığını görmeye çalışıyorum; iki ayrı liste aynı ise değil.
maksimum

4

Listeyi kümeye dönüştürün ve ardından kümedeki öğelerin sayısını bulun. Sonuç 1 ise, aynı öğelere sahiptir ve eğer değilse, listedeki öğeler aynı değildir.

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

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

4

İle kullanımı reduce()ile ilgili lambda. Kişisel olarak diğer cevaplardan daha güzel olduğunu düşündüğüm bir çalışma kodu.

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

Tüm öğeler aynı veya değilse, ilk değerin boole olduğu bir demet döndürür.


Kodda yazıldığı gibi küçük bir hata var (deneyin [1, 2, 2]): önceki boole değerini dikkate almaz. Bu urun, sabitlenebilir x[1] == yile x[0] and x[1] == y.
schot

3

Ben yapardım:

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

olarak anydurur en kısa sürede iterable arama bir bulduğu olarak Truekoşulu.


Tek argüman ise, jeneratör ifadesinin etrafındaki ekstra parantezlere ihtiyacınız yoktur.
ninjagecko

böylece yok all(), neden kullanmaz all(x == seq[0] for x in seq)? daha pythonic görünüyor ve aynı gerçekleştirmek gerekir
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

"Tümü" olmayan Python 2.4 ile çalışır.


1
for k in j: breakeşittir next(j). def allTheSame(x): return len(list(itertools.groupby(x))<2)Verimliliği önemsemediyseniz de yapmış olabilirsiniz.
ninjagecko

2

Harita ve lambda kullanabilir

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

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

2

Veya diffnumpy yöntemini kullanın :

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

Ve aramak için:

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

Çıktı:

True

Bence not np.any(np.diff(l))biraz daha hızlı olabilir.
GZ0

2

Veya farklı numpy yöntemini kullanın:

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

Ve aramak için:

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

Çıktı:

Doğru


Bu cevap geçen yıldan U9-Forward'ın cevabı ile aynı.
mhwombat

Güzel göz! Aynı yapıyı / API'yı kullandım, ancak yöntemim np.unique ve shape kullanıyor. U9 işlevi np.all () ve np.diff () kullanır - Bu işlevlerden hiçbirini kullanmıyorum.
Luis B

1

Yapabilirsin:

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

Python'un operatörleri içe aktarmanızı oldukça sinir bozucu operator.and_. Python3'ten itibaren ayrıca içe aktarmanız gerekecektir functools.reduce.

(Bu yöntemi kullanmamalısınız, çünkü eşit olmayan değerler bulursa kırılmaz, ancak tüm listeyi incelemeye devam eder. Burada tamlık için bir yanıt olarak eklenmiştir.)


Bu kısa devre yapmaz. Neden diğer çözümünüze tercih edesiniz ki?
maks.

@max: tam olarak bu nedenle yapmazsınız; Bütünlük uğruna dahil ettim. Muhtemelen bunu belirtmek için düzenlemeliyim, teşekkürler.
ninjagecko

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

Bir sonraki kısa devreye kısa devre yapacak:

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

İlk kodunuz yanlıştı: reduce(lambda a,b:a==b, [2,2,2])verimler False... Ben düzenledim, ama bu şekilde artık hoş değil
berdario

@berdario O zaman başka birinin yazdıklarını değiştirmek yerine kendi cevabınızı yazmış olmalısınız. Bu cevabın yanlış olduğunu düşünüyorsanız, yorum yapabilir ve / veya aşağıya doğru oy verebilirsiniz.
Gorpik

3
Yanlış bir şeyi düzeltmek daha iyidir, tüm insanların okuması için orada bırakmaktan, bunun neden yanlış olduğunu açıklayan yorumları
kaçırmaktan daha iyidir

3
"Yayınları ne zaman düzenlemeliyim?" "Gönderiyi daha iyi yapabileceğinizi ve bunu yapmaya meyilli olduğunuzu hissettiğiniz her zaman. Düzenleme yapılması teşvik edilir!"
berdario

1

Listeyi bir kümeyle değiştirin. O zaman setin boyutu sadece 1 ise, aynı olmalılar.

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

1

Saf Python özyinelemeli bir seçenek de vardır:

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

Ancak, bazı nedenlerden ötürü, diğer seçeneklerden daha yavaş iki büyüklük sırasıdır. C dili zihniyetinden geldiğimde, bunun daha hızlı olmasını bekliyordum, ama değil!

Diğer dezavantaj, Python'da bu durumda ayarlanması gereken özyineleme sınırının olmasıdır. Örneğin bunu kullanarak .


0

.nunique()Listedeki benzersiz öğelerin sayısını bulmak için kullanabilirsiniz .

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

kullanabilirsiniz set. Bir set yapacak ve tekrarlayan elemanları kaldıracaktır. Sonra 1'den fazla eleman olmadığını kontrol edin.

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

Misal:

>>> len(set([5, 5])) <= 1
True
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.