Periksa apakah semua elemen dalam daftar identik

390

Saya membutuhkan fungsi berikut:

Input : alist

Keluaran :

  • True jika semua elemen dalam daftar input mengevaluasi sama satu sama lain menggunakan operator kesetaraan standar;
  • False jika tidak.

Kinerja : tentu saja, saya lebih suka untuk tidak mengeluarkan biaya tidak perlu.

Saya merasa akan lebih baik untuk:

  • beralih melalui daftar
  • membandingkan elemen yang berdekatan
  • dan ANDsemua nilai Boolean yang dihasilkan

Tapi saya tidak yakin apa cara paling Pythonic untuk melakukan itu.


Kurangnya fitur hubung singkat hanya menyakitkan pada input panjang (lebih dari ~ 50 elemen) yang memiliki elemen yang tidak sama sejak awal. Jika ini terjadi cukup sering (seberapa sering tergantung pada berapa lama daftar mungkin), korsleting diperlukan. Algoritme korsleting terbaik tampaknya adalah @ KennyTM checkEqual1. Namun, membayar biaya yang signifikan untuk ini:

  • hingga 20x dalam daftar kinerja hampir identik
  • kinerja hingga 2,5x pada daftar pendek

Jika input panjang dengan elemen awal yang tidak sama tidak terjadi (atau jarang terjadi), korsleting tidak diperlukan. Kemudian, sejauh ini yang tercepat adalah solusi @Ivo van der Wijk.

maks
sumber
3
Sama dengan a == batau identik dengan di a is b?
kennytm
1
Haruskah solusi menangani daftar kosong? Jika demikian, apa yang harus dikembalikan?
Doug
1
Sama seperti dalam a == b. Harus menangani daftar kosong, dan mengembalikan True.
maks
2
Meskipun saya tahu ini lebih lambat daripada beberapa rekomendasi lainnya, saya terkejut functools.reduce(operator.eq, a)belum disarankan.
user2846495

Jawaban:

420

Metode umum:

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

Satu-liner:

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

Juga satu-liner:

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

Perbedaan antara 3 versi adalah:

  1. Dalam checkEqual2konten harus hashable.
  2. checkEqual1dan checkEqual2dapat menggunakan iterator apa pun, tetapi checkEqual3harus mengambil input berurutan, biasanya wadah beton seperti daftar atau tupel.
  3. checkEqual1 berhenti segera setelah perbedaan ditemukan.
  4. Karena checkEqual1mengandung lebih banyak kode Python, itu kurang efisien ketika banyak item sama di awal.
  5. Karena checkEqual2dan checkEqual3selalu melakukan operasi penyalinan O (N), itu akan memakan waktu lebih lama jika sebagian besar input Anda akan mengembalikan False.
  6. Untuk checkEqual2dan checkEqual3lebih sulit untuk menyesuaikan perbandingan dari a == bke a is b.

timeit hasil, untuk Python 2.7 dan (hanya s1, s4, s7, s9 harus mengembalikan 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 = []

kita mendapatkan

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

catatan:

# 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
kennytm
sumber
1
Terima kasih, ini adalah penjelasan alternatif yang sangat membantu. Bisakah Anda periksa ulang tabel kinerja Anda - apakah semuanya dalam msec, dan apakah angka-angka di sel yang benar?
maks
7
@ Max: Ya. Perhatikan bahwa 1 msec = 1000 usec.
kennytm
1
Jangan lupa analisis penggunaan memori untuk array yang sangat besar, solusi asli yang mengoptimalkan panggilan obj.__eq__saat lhs is rhs, dan optimisasi out-of-order untuk memungkinkan daftar diurutkan hubungan pendek lebih cepat.
Glenn Maynard
3
Ivo van der Wijk memiliki solusi yang lebih baik untuk urutan yang sekitar 5 kali lebih cepat daripada yang ditetapkan dan O (1) dalam memori.
aaronasterling
2
Ada juga itertoolsresep yang saya tambahkan sebagai jawaban. Mungkin ada baiknya melemparkan itu ke dalam matriks waktu Anda :-).
mgilson
301

Solusi yang lebih cepat daripada menggunakan set () yang bekerja pada urutan (bukan iterables) adalah cukup menghitung elemen pertama. Ini mengasumsikan bahwa daftar ini tidak kosong (tapi itu sepele untuk diperiksa, dan tentukan sendiri apa hasilnya di daftar kosong)

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

beberapa tolok ukur sederhana:

>>> 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
Ivo van der Wijk
sumber
5
OMG, ini 6 kali lebih cepat dari solusi yang ditetapkan! (280 juta elemen / detik vs 45 juta elemen / detik di laptop saya). Mengapa??? Dan apakah ada cara untuk memodifikasinya sehingga sirkuit pendek (saya kira tidak ...)
maks
2
Saya kira list.count memiliki implementasi C yang sangat optimal, dan panjang daftar disimpan secara internal, jadi len () juga murah. Tidak ada cara untuk menghitung arus pendek () karena Anda harus benar-benar memeriksa semua elemen untuk mendapatkan jumlah yang benar.
Ivo van der Wijk
Bisakah saya mengubahnya ke: x.count(next(x)) == len(x)sehingga berfungsi untuk setiap wadah x? Ahh .. nm, baru saja melihat .count hanya tersedia untuk sekuens .. Mengapa tidak diterapkan untuk wadah bawaan lainnya? Apakah menghitung di dalam kamus secara inheren kurang berarti daripada di dalam daftar?
maks
4
Sebuah iterator mungkin tidak memiliki panjang. Misalnya bisa tak terbatas atau hanya dihasilkan secara dinamis. Anda hanya dapat menemukan panjangnya dengan mengonversinya menjadi daftar yang menghilangkan sebagian besar keuntungan iterator
Ivo van der Wijk
Maaf, yang saya maksudkan adalah mengapa counttidak diterapkan untuk iterables, bukan mengapa lentidak tersedia untuk iterators. Jawabannya mungkin itu hanya kekhilafan. Tapi itu tidak relevan bagi kami karena standar .count()untuk urutan sangat lambat (python murni). Alasan solusi Anda begitu cepat adalah bahwa itu bergantung pada C-diimplementasikan yang countdisediakan oleh list. Jadi saya kira mana saja yang terjadi untuk menerapkan countmetode dalam C akan mendapat manfaat dari pendekatan Anda.
Maks
164

Cara paling sederhana dan paling elegan adalah sebagai berikut:

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

(Ya, ini bahkan berfungsi dengan daftar kosong! Ini karena ini adalah salah satu dari sedikit kasus di mana python memiliki semantik malas.)

Mengenai kinerja, ini akan gagal sedini mungkin, sehingga asimtotik optimal.

ninjagecko
sumber
Ini berfungsi, tetapi sedikit (1,5x) lebih lambat dari @ KennyTM checkEqual1. Saya tidak yakin mengapa.
maks.
4
maks: Mungkin karena saya tidak repot-repot melakukan optimasi first=myList[0] all(x==first for x in myList), mungkin
ninjagecko
Saya pikir myList [0] dievaluasi dengan setiap iterasi. >>> timeit.timeit ('all ([y == x [0] untuk y in x])', 'x = [1] * 4000', angka = 10000) 2.707076672740641 >>> timeit.timeit ('x0 = x [0]; semua ([y == x0 untuk y di x]) ',' x = [1] * 4000 ', number = 10000) 2.0908854261426484
Matt Liberty
1
Tentu saja saya harus mengklarifikasi bahwa optimasi first=myList[0]akan melempar IndexErrorpada daftar kosong, jadi komentator yang berbicara tentang optimasi yang saya sebutkan harus berurusan dengan kasus tepi dari daftar kosong. Namun yang asli baik-baik saja (baik x==myList[0]-baik saja di dalam allkarena tidak pernah dievaluasi jika daftar kosong).
ninjagecko
1
Ini jelas cara yang tepat untuk melakukannya. Jika Anda ingin kecepatan dalam setiap kasus, gunakan sesuatu seperti numpy.
Henry Gomersall
45

Pekerjaan perbandingan yang ditetapkan:

len(set(the_list)) == 1

Menggunakan setmenghapus semua elemen duplikat.

cbalawat
sumber
26

Anda dapat mengubah daftar menjadi satu set. Satu set tidak dapat memiliki duplikat. Jadi jika semua elemen dalam daftar asli identik, set akan memiliki hanya satu elemen.

if len(sets.Set(input_list)) == 1
// input_list has all identical elements.
codaddict
sumber
ini bagus tetapi tidak mengalami hubungan pendek dan Anda harus menghitung panjang daftar yang dihasilkan.
aaronasterling
15
kenapa tidak adil len(set(input_list)) == 1?
Nick Dandoulakis
2
@codaddict. Ini berarti bahwa meskipun dua elemen pertama berbeda, itu masih akan menyelesaikan seluruh pencarian. itu juga menggunakan O (k) ruang ekstra di mana k adalah jumlah elemen yang berbeda dalam daftar.
aaronasterling
1
@max. karena membangun set terjadi di C dan Anda memiliki implementasi yang buruk. Setidaknya Anda harus melakukannya dalam ekspresi generator. Lihat jawaban KennyTM untuk cara melakukannya dengan benar tanpa menggunakan perangkat.
aaronasterling
1
sets.Set adalah "Tidak digunakan sejak versi 2.6: Tipe set / frozenset bawaan menggantikan modul ini." (dari docs.python.org/2/library/sets.html )
Moberg
21

Untuk apa nilainya, ini muncul di milis python-ideas baru-baru ini. Ternyata sudah ada resep itertools untuk melakukan ini: 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)

Seharusnya berkinerja sangat baik dan memiliki beberapa properti bagus.

  1. Sirkuit pendek: Ini akan berhenti mengkonsumsi item dari iterable segera setelah ia menemukan item non-sama pertama.
  2. Tidak memerlukan item untuk di hashable.
  3. Itu malas dan hanya membutuhkan O (1) memori tambahan untuk melakukan pemeriksaan.

1 Dengan kata lain, saya tidak dapat mengambil kredit untuk datang dengan solusi - saya juga tidak dapat mengambil kredit untuk bahkan menemukannya .

mgilson
sumber
3
Jauh lebih cepat daripada jawaban tercepat yang tercantum di sini dalam skenario terburuk.
ChaimG
return next(g, f := next(g, g)) == f(dari py3.8 tentu saja)
Chris_Rands
17

Berikut adalah dua cara sederhana untuk melakukan ini

menggunakan set ()

Saat mengonversi daftar ke set, elemen duplikat dihapus. Jadi jika panjang himpunan dikonversi adalah 1, maka ini berarti bahwa semua elemen adalah sama.

len(set(input_list))==1

Berikut ini sebuah contoh

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

menggunakan semua ()

Ini akan membandingkan (ekivalensi) elemen pertama dari daftar input dengan setiap elemen lain dalam daftar. Jika semua yang ekuivalen Benar akan dikembalikan, jika tidak Palsu akan dikembalikan.

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

Berikut ini sebuah contoh

>>> 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 Jika Anda memeriksa untuk melihat apakah seluruh daftar setara dengan nilai tertentu, Anda dapat suibstitue nilai dalam untuk input_list [0].

Christopher Nuccio
sumber
1
Bagi orang-orang yang tertarik pada runtime, melakukan len(set(a))pada daftar 10.000.000 elemen membutuhkan waktu 0,09 detik sedangkan performa allmembutuhkan waktu 0,9 detik (10 kali lebih lama).
Elliptica
2
Saya juga menyukai jawaban ini karena kesederhanaannya yang pythonic, di samping skor kinerja yang disebutkan oleh @ Eliptica
NickBraunagel
11

Ini adalah pilihan lain, lebih cepat daripada len(set(x))==1daftar panjang (menggunakan hubungan pendek)

def constantList(x):
    return x and [x[0]]*len(x) == x
6502
sumber
Ini 3 kali lebih lambat dari solusi yang ditetapkan pada komputer saya, mengabaikan korsleting. Jadi, jika elemen yang tidak sama ditemukan rata-rata di sepertiga pertama daftar, itu rata-rata lebih cepat.
maks
9

Ini adalah cara sederhana untuk melakukannya:

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

Ini sedikit lebih rumit, itu mengeluarkan fungsi panggilan overhead, tetapi semantik lebih jelas dijabarkan:

def all_identical(seq):
    if not seq:
        # empty list is False.
        return False
    first = seq[0]
    return all(first == elem for elem in seq)
Jerub
sumber
Anda dapat menghindari perbandingan yang berlebihan di sini dengan menggunakan for elem in mylist[1:]. Keraguan itu meningkatkan kecepatan jauh karena saya kira elem[0] is elem[0]sehingga penerjemah mungkin dapat melakukan perbandingan itu dengan sangat cepat.
Brendan
5

Periksa apakah semua elemen sama dengan yang pertama.

np.allclose(array, array[0])

Gusev Slava
sumber
Membutuhkan modul pihak ketiga.
Bachsau
4

Keraguan ini adalah "yang paling Pythonic", tetapi sesuatu seperti:

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

akan melakukan trik.

machineghost
sumber
1
forLingkaran Anda dapat dibuat menjadi lebih Pythonic if any(item != list[0] for item in list[1:]): return False, dengan semantik yang persis sama.
musiphil
4

Jika Anda tertarik pada sesuatu yang sedikit lebih mudah dibaca (tapi tentu saja tidak seefisien,) Anda dapat mencoba:

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
Joshua Burns
sumber
Saya sebenarnya mencoba melihat apakah semua elemen dalam satu daftar identik; tidak jika dua daftar terpisah identik.
Maks
4

Ubah daftar menjadi himpunan dan kemudian temukan jumlah elemen dalam himpunan. Jika hasilnya 1, memiliki elemen identik dan jika tidak, maka elemen dalam daftar tidak identik.

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

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

Mengenai menggunakan reduce()dengan lambda. Berikut adalah kode kerja yang menurut saya pribadi jauh lebih baik daripada beberapa jawaban lainnya.

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

Mengembalikan tuple di mana nilai pertama adalah boolean jika semua item sama atau tidak.

Marcus Lind
sumber
Ada kesalahan kecil dalam kode seperti yang tertulis (coba [1, 2, 2]): tidak memperhitungkan nilai boolean sebelumnya. Ini dapat diperbaiki dengan mengganti x[1] == ydengan x[0] and x[1] == y.
schot
3

Saya akan melakukan:

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

sebagai anyberhenti mencari iterable secepat itu menemukan Truekondisi.

Robert Rossney
sumber
Anda tidak perlu tanda kurung tambahan di sekitar ekspresi generator jika itu satu-satunya argumen.
ninjagecko
begitu juga all(), mengapa tidak digunakan all(x == seq[0] for x in seq)? terlihat lebih pythonic dan harus melakukan hal yang sama
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"
pyfunc
sumber
2
def allTheSame(i):
    j = itertools.groupby(i)
    for k in j: break
    for k in j: return False
    return True

Bekerja di Python 2.4, yang tidak memiliki "semua".

itertool
sumber
1
for k in j: breaksetara dengan next(j). Anda juga bisa melakukannya def allTheSame(x): return len(list(itertools.groupby(x))<2)jika Anda tidak peduli dengan efisiensi.
ninjagecko
2

Dapat menggunakan peta dan lambda

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

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

Atau gunakan diffmetode numpy:

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

Dan untuk menelepon:

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

Keluaran:

True
U10-Maju
sumber
Saya pikir not np.any(np.diff(l))bisa sedikit lebih cepat.
GZ0
2

Atau gunakan metode beda numpy:

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

Dan untuk menelepon:

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

Keluaran:

Benar

Luis B
sumber
Jawaban ini identik dengan jawaban dari U9-Forward dari tahun lalu.
mhwombat
Mata yang bagus! Saya menggunakan struktur / API yang sama, tetapi metode saya menggunakan np.unique dan bentuk. Fungsi U9 menggunakan np.all () dan np.diff () - Saya tidak menggunakan salah satu dari fungsi tersebut.
Luis B
1

Anda dapat melakukan:

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

Agak menyebalkan bahwa python membuat Anda mengimpor seperti operator operator.and_. Pada python3, Anda juga harus mengimpor functools.reduce.

(Anda tidak boleh menggunakan metode ini karena tidak akan rusak jika menemukan nilai yang tidak sama, tetapi akan terus memeriksa seluruh daftar. Itu hanya dimasukkan di sini sebagai jawaban untuk kelengkapan.)

ninjagecko
sumber
Ini tidak akan korsleting. Mengapa Anda lebih suka daripada solusi Anda yang lain?
Maks
@ Max: Anda tidak akan, tepatnya karena alasan itu; Saya memasukkannya demi kelengkapan. Saya mungkin harus mengeditnya untuk menyebutkan itu, terima kasih.
ninjagecko
1
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]

Yang berikutnya akan mengalami hubungan pendek:

all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))
pengguna3015260
sumber
Kode pertama Anda jelas salah: reduce(lambda a,b:a==b, [2,2,2])menghasilkan False... Saya mengeditnya, tetapi cara ini tidak cantik lagi
berdario
@berdario Maka Anda seharusnya menulis jawaban Anda sendiri, daripada mengubah apa yang ditulis orang lain. Jika Anda merasa jawaban ini salah, Anda dapat mengomentarinya dan / atau membatalkannya.
Gorpik
3
Lebih baik memperbaiki sesuatu yang salah, daripada membiarkannya di sana agar semua orang membacanya, mungkin kehilangan komentar yang menjelaskan mengapa itu salah
berdario
3
"Kapan saya harus mengedit posting?" "Kapan pun Anda merasa dapat memperbaiki pos, dan cenderung melakukannya. Editing dianjurkan!"
berdario
1

Ubah daftar menjadi satu set. Kemudian jika ukuran set hanya 1, mereka pasti sama.

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

Ada juga opsi rekursif Python murni:

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

Namun untuk beberapa alasan, dalam beberapa kasus dua urutan besarnya lebih lambat dari opsi lain. Berasal dari mentalitas bahasa C, saya mengharapkan ini lebih cepat, tetapi ternyata tidak!

Kerugian lainnya adalah bahwa ada batas rekursi dalam Python yang perlu disesuaikan dalam kasus ini. Misalnya menggunakan ini .

Isi
sumber
0

Anda dapat menggunakan .nunique()untuk menemukan sejumlah item unik dalam daftar.

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

Anda dapat menggunakan set. Ini akan membuat set dan menghapus elemen berulang. Kemudian periksa apakah ia memiliki tidak lebih dari 1 elemen.

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

Contoh:

>>> len(set([5, 5])) <= 1
True
MHB
sumber