Apakah ada built-in yang menghapus duplikat dari daftar di Python, sambil menjaga ketertiban? Saya tahu bahwa saya bisa menggunakan satu set untuk menghapus duplikat, tetapi itu merusak tatanan asli. Saya juga tahu bahwa saya dapat menggulung sendiri seperti ini:
def uniq(input):
output = []
for x in input:
if x not in output:
output.append(x)
return output
(Terima kasih kepada bersantai untuk itu sampel kode .)
Tetapi saya ingin memanfaatkan idiom bawaan atau lebih Pythonic jika memungkinkan.
Pertanyaan terkait: Dengan Python, apa algoritma tercepat untuk menghapus duplikat dari daftar sehingga semua elemen unik sekaligus menjaga ketertiban ?
sumber
seen.add
bisa saja berubah di antara iterasi, dan runtime tidak cukup pintar untuk mengesampingkan itu. Untuk bermain aman, ia harus memeriksa objek setiap kali. - Jika Anda melihat bytecode dengandis.dis(f)
, Anda dapat melihat bahwa bytecode dieksekusiLOAD_ATTR
untukadd
anggota pada setiap iterasi. ideone.com/tz1Tllseen_add
merupakan peningkatan tetapi pengaturan waktu dapat dipengaruhi oleh sumber daya sistem pada saat itu. Akan tertarik untuk melihat timing penuhseen_add = seen.add
hasil hanya peningkatan kecepatan 1%. Ini hampir tidak signifikan.Edit 2016
Seperti yang ditunjukkan Raymond , dalam python 3.5+ di mana
OrderedDict
diimplementasikan dalam C, pendekatan pemahaman daftar akan lebih lambat daripadaOrderedDict
(kecuali Anda benar-benar membutuhkan daftar di akhir - dan bahkan kemudian, hanya jika inputnya sangat pendek). Jadi solusi terbaik untuk 3.5+ adalahOrderedDict
.Edit Penting 2015
Seperti yang dicatat @abarnert ,
more_itertools
library (pip install more_itertools
) berisiunique_everseen
fungsi yang dibangun untuk menyelesaikan masalah ini tanpa mutasi yang tidak dapat dibaca (not seen.add
) dalam pemahaman daftar. Ini juga merupakan solusi tercepat:Hanya satu impor perpustakaan sederhana dan tidak ada retasan. Ini berasal dari implementasi resep itertools
unique_everseen
yang terlihat seperti:Dalam Python
2.7+
yangidiom umum diterima(yang bekerja tetapi tidak dioptimalkan untuk kecepatan, saya sekarang akan menggunakanunique_everseen
) untuk keperluan inicollections.OrderedDict
:Runtime: O (N)
Ini terlihat jauh lebih bagus daripada:
dan tidak memanfaatkan hack jelek :
yang bergantung pada fakta bahwa
set.add
ini adalah metode in-place yang selalu mengembalikanNone
sehingganot None
dievaluasiTrue
.Namun perlu dicatat bahwa solusi peretasan lebih cepat dalam kecepatan mentah meskipun memiliki kompleksitas runtime yang sama O (N).
sumber
[seen.add(x) for x in seq if x not in seen]
, atau jika Anda tidak suka efek samping pemahaman cukup gunakan satufor
loop:for x in seq: seen.add(x) if x not in seen else None
(masih satu-liner, meskipun dalam hal ini saya pikir satu-liner-ness adalah properti konyol untuk mencoba memiliki dalam solusiseen = set(seq)
.Dalam Python 2.7 , cara baru untuk menghapus duplikat dari iterable sambil menjaganya dalam urutan asli adalah:
Dalam Python 3.5 , OrderedDict memiliki implementasi C. Pengaturan waktu saya menunjukkan bahwa ini sekarang adalah yang tercepat dan terpendek dari berbagai pendekatan untuk Python 3.5.
Dalam Python 3.6 , perintah reguler menjadi teratur dan kompak. (Fitur ini berlaku untuk CPython dan PyPy tetapi mungkin tidak ada dalam implementasi lain). Itu memberi kami cara deduksi tercepat baru sambil mempertahankan pesanan:
Dalam Python 3.7 , dikt reguler dijamin untuk keduanya dipesan di semua implementasi. Jadi, solusi terpendek dan tercepat adalah:
Tanggapan untuk @max: Setelah Anda pindah ke 3.6 atau 3.7 dan menggunakan dict biasa alih-alih OrderedDict , Anda tidak bisa benar-benar mengalahkan kinerja dengan cara lain. Kamusnya padat dan siap dikonversi ke daftar dengan hampir tanpa overhead. Daftar target adalah pra-ukuran untuk len (d) yang menyimpan semua ukuran yang terjadi dalam pemahaman daftar. Juga, karena daftar kunci internal padat, menyalin pointer hampir secepat salinan daftar.
sumber
OrderedDict
ke daftar pada akhirnya. Jika saya perlu mengubahnya ke daftar, untuk input kecil pendekatan pemahaman daftar masih lebih cepat hingga 1,5 kali. Yang mengatakan, solusi ini jauh lebih bersih.set()
akan membantu lebih banyak pengguna yang naif mengembangkan kode yang dapat direproduksi.unik →
['1', '2', '3', '6', '4', '5']
sumber
n^2
None
referensi lain dalam proses!)for
lingkaran sebagai gantinyaBukan untuk menendang kuda mati (pertanyaan ini sudah sangat tua dan sudah memiliki banyak jawaban bagus), tetapi di sini ada solusi menggunakan panda yang cukup cepat dalam banyak keadaan dan mati mudah digunakan.
sumber
Daftar itu bahkan tidak harus disortir , syarat yang memadai adalah bahwa nilai yang sama dikelompokkan bersama.
Sunting: Saya berasumsi bahwa "menjaga pesanan" menyiratkan bahwa daftar sebenarnya dipesan. Jika ini bukan masalahnya, maka solusi dari MizardX adalah yang benar.
Suntingan komunitas: Ini adalah cara paling elegan untuk "mengompres elemen duplikat berurutan menjadi satu elemen".
sumber
Saya pikir jika Anda ingin mempertahankan pesanan,
Anda dapat mencoba ini:
ATAU sama halnya Anda dapat melakukan ini:
Anda juga dapat melakukan ini:
Dapat juga ditulis sebagai berikut:
sumber
Dalam Python 3.7 dan di atasnya, kamus dijamin untuk mengingat urutan penyisipan kuncinya. Jawaban atas pertanyaan ini merangkum keadaan saat ini.
The
OrderedDict
solusi sehingga menjadi usang dan tanpa pernyataan impor kita hanya bisa mengeluarkan:sumber
Untuk jawaban yang sangat terlambat untuk pertanyaan lain yang sangat lama:
The
itertools
resep memiliki fungsi yang melakukan ini, dengan menggunakanseen
teknik set, tetapi:key
fungsi standar .seen.add
alih - alih mencari N kali. (f7
juga melakukan ini, tetapi beberapa versi tidak.)ifilterfalse
, jadi Anda hanya perlu mengulang elemen unik di Python, bukan semuanya. (Anda masih mengulanginya semua di dalamifilterfalse
, tentu saja, tapi itu dalam C, dan jauh lebih cepat.)Apakah ini sebenarnya lebih cepat daripada
f7
? Tergantung pada data Anda, jadi Anda harus mengujinya dan melihatnya. Jika Anda ingin daftar pada akhirnya,f7
gunakan listcomp, dan tidak ada cara untuk melakukannya di sini. (Anda bisa langsungappend
bukannyayield
, atau Anda bisa memberi makan generator ke dalamlist
fungsi, tetapi tidak ada yang bisa secepat LIST_APPEND di dalam listcomp.) Bagaimanapun, biasanya, memeras beberapa mikrodetik tidak akan menjadi seperti penting sebagai memiliki fungsi yang mudah dimengerti, dapat digunakan kembali, sudah ditulis yang tidak memerlukan DSU ketika Anda ingin menghias.Seperti semua resep, itu juga tersedia di
more-iterools
.Jika Anda hanya menginginkan no-
key
case, Anda dapat menyederhanakannya sebagai:sumber
more-itertools
ini jelas jawaban terbaik. Sebuahfrom more_itertools import unique_everseen
list(unique_everseen(items))
pendekatan sederhana yang jauh lebih cepat daripada saya dan jauh lebih baik daripada jawaban yang diterima, saya pikir download perpustakaan sepadan. Saya akan ke komunitas wiki jawaban saya dan menambahkan ini.Hanya untuk menambah (sangat performant) pelaksanaan fungsi suatu tersebut dari modul eksternal 1 :
iteration_utilities.unique_everseen
:Pengaturan waktu
Saya melakukan beberapa pengaturan waktu (Python 3.6) dan ini menunjukkan bahwa ini lebih cepat daripada semua alternatif lain yang saya uji, termasuk
OrderedDict.fromkeys
,f7
danmore_itertools.unique_everseen
:Dan hanya untuk memastikan saya juga melakukan tes dengan duplikat lebih banyak hanya untuk memeriksa apakah ada bedanya:
Dan satu yang hanya mengandung satu nilai:
Dalam semua kasus ini
iteration_utilities.unique_everseen
fungsinya adalah yang tercepat (di komputer saya).Ini
iteration_utilities.unique_everseen
fungsi juga dapat menangani nilai-nilai unhashable pada input (namun denganO(n*n)
kinerja bukanO(n)
kinerja ketika nilai-nilai yang hashable).1 Penafian: Saya pembuat paket itu.
sumber
seen_add = seen.add
- apakah ini diperlukan untuk tolok ukur?dict.fromkeys()
metode ke bagan Anda?ordereddict.fromkeys
?Tanpa tipe hashable (mis. Daftar daftar), berdasarkan MizardX's:
sumber
Meminjam ide rekursif yang digunakan dalam mendefinisikan
nub
fungsi Haskell untuk daftar, ini akan menjadi pendekatan rekursif:misalnya:
Saya mencoba untuk menumbuhkan ukuran data dan melihat kompleksitas waktu sub-linear (tidak pasti, tetapi menyarankan ini harus baik untuk data normal).
Saya juga berpikir itu menarik bahwa ini dapat dengan mudah digeneralisasikan ke keunikan oleh operasi lain. Seperti ini:
Misalnya, Anda bisa meneruskan fungsi yang menggunakan gagasan pembulatan ke bilangan bulat yang sama seolah-olah itu "kesetaraan" untuk tujuan keunikan, seperti ini:
kemudian unik (some_list, test_round) akan memberikan elemen unik dari daftar di mana keunikan tidak lagi berarti kesetaraan tradisional (yang tersirat dengan menggunakan segala jenis pendekatan berbasis set atau dict-kunci berbasis masalah ini) tetapi sebaliknya dimaksudkan untuk mengambil hanya elemen pertama yang membulatkan ke K untuk setiap kemungkinan bilangan bulat K yang mungkin membulat, misalnya:
sumber
filter
hampir tidak akan mendapat manfaat dari panggilan sebelumnya sama sekali. Tetapi jika jumlah elemen unik relatif kecil terhadap ukuran array, ini akan berkinerja cukup baik.5 x lebih cepat mengurangi varian tetapi lebih canggih
Penjelasan:
sumber
Anda dapat merujuk pemahaman daftar karena sedang dibangun oleh simbol '_ [1]'.
Misalnya, fungsi berikut unik-ifies daftar elemen tanpa mengubah urutannya dengan merujuk pemahaman daftar.
Demo:
Keluaran:
sumber
Jawaban MizardX memberikan koleksi yang baik dari berbagai pendekatan.
Inilah yang saya pikirkan sambil berpikir keras:
sumber
O(n)
operasi dan Anda melakukannya pada setiap item, kompleksitas yang dihasilkan dari solusi Anda akan menjadiO(n^2)
. Ini hanya tidak bisa diterima untuk masalah sepele seperti itu.di sini adalah cara sederhana untuk melakukannya:
yang memberikan output:
sumber
Anda bisa melakukan semacam hack daftar pemahaman jelek.
sumber
i,e in enumerate(l)
untukl[i] for i in range(len(l))
.Pendekatan yang relatif efektif dengan
_sorted_
sebuahnumpy
array:Output:
sumber
Ekspresi generator yang menggunakan O (1) mencari set untuk menentukan apakah akan memasukkan elemen dalam daftar baru atau tidak.
sumber
extend
dengan ekspresi generator yang bergantung pada hal yang sedang diperluas (jadi +1), tetapiset(n)
dihitung ulang pada setiap tahap (yang linier) dan ini membuat pendekatan keseluruhan menjadi kuadratik. Bahkan, ini hampir pasti lebih buruk daripada hanya menggunakanele in n
. Membuat set untuk tes keanggotaan tunggal tidak sebanding dengan biaya pembuatan set. Tetap saja - ini merupakan pendekatan yang menarik.Solusi rekursif sederhana:
sumber
Menghilangkan nilai duplikat secara berurutan, tetapi mempertahankan urutan item yang tersisa. Penggunaan fungsi generator tujuan umum.
sumber
pengguna panda harus memeriksa
pandas.unique
.Fungsi mengembalikan array NumPy. Jika perlu, Anda dapat mengonversinya menjadi daftar dengan
tolist
metode ini.sumber
Jika Anda membutuhkan satu liner maka mungkin ini akan membantu:
... harus bekerja tetapi koreksi saya jika saya salah
sumber
Jika Anda secara rutin menggunakan
pandas
, dan estetika lebih disukai daripada kinerja, maka pertimbangkan fungsi bawaanpandas.Series.drop_duplicates
:Pengaturan waktu:
sumber
ini akan menjaga ketertiban dan berjalan dalam waktu O (n). pada dasarnya idenya adalah membuat lubang di mana pun ada duplikat ditemukan dan menenggelamkannya ke bawah. memanfaatkan pointer baca dan tulis. setiap kali duplikat ditemukan hanya pointer baca maju dan tulis pointer tetap pada entri duplikat untuk menimpa itu.
sumber
Solusi tanpa menggunakan modul atau set yang diimpor:
Memberikan output:
sumber
Metode di tempat
Metode ini kuadratik, karena kami memiliki pencarian linier ke dalam daftar untuk setiap elemen daftar (untuk itu kami harus menambahkan biaya menata ulang daftar karena
del
s).Yang mengatakan, adalah mungkin untuk beroperasi di tempat jika kita mulai dari akhir daftar dan melanjutkan ke asal menghapus setiap istilah yang ada di sub-daftar di sebelah kirinya
Ide dalam kode ini sederhana
Tes implementasi yang sederhana
sumber
l[:] = <one of the the faster methods>
jika Anda menginginkan operasi di tempat, bukan?a=[1]; b=a; a[:]=[2]
makab==[2]
nilainya adalahTrue
dan kita dapat mengatakan bahwa kita melakukannya di tempat, namun apa yang Anda usulkan menggunakan ruang baru untuk memiliki daftar baru, ganti data lama dengan data baru dan tandai data lama untuk pengumpulan sampah karena tidak lagi direferensikan oleh apa pun, jadi mengatakan itu beroperasi di tempat adalah sedikit meregangkan konsep wrt apa yang saya tunjukkan adalah mungkin ... apakah itu tidak efisien? ya, tapi saya sudah katakan sebelumnya.Pendekatan zmk menggunakan pemahaman daftar yang sangat cepat, namun menjaga urutan secara alami. Untuk menerapkan string case sensitif dapat dengan mudah dimodifikasi. Ini juga mempertahankan kasus aslinya.
Fungsi yang terkait erat adalah:
sumber
Pemahaman daftar satu liner:
Cukup tambahkan persyaratan untuk memeriksa bahwa nilai tidak pada posisi sebelumnya
sumber