Saya memiliki daftar daftar dengan Python:
k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
Dan saya ingin menghapus elemen duplikat darinya. Apakah jika itu daftar normal bukan daftar yang bisa saya gunakan set
. Namun sayangnya daftar tersebut tidak dapat di-hash dan tidak dapat dijadikan kumpulan daftar. Hanya tupel. Jadi saya bisa mengubah semua daftar menjadi tupel kemudian menggunakan set dan kembali ke daftar. Tapi ini tidak cepat.
Bagaimana ini bisa dilakukan dengan cara yang paling efisien?
Hasil dari daftar di atas seharusnya:
k = [[5, 6, 2], [1, 2], [3], [4]]
Saya tidak peduli tentang menjaga ketertiban.
Catatan: pertanyaan ini serupa tetapi tidak sesuai dengan yang saya butuhkan. Mencari SO tetapi tidak menemukan duplikat yang tepat.
Pembandingan:
import itertools, time
class Timer(object):
def __init__(self, name=None):
self.name = name
def __enter__(self):
self.tstart = time.time()
def __exit__(self, type, value, traceback):
if self.name:
print '[%s]' % self.name,
print 'Elapsed: %s' % (time.time() - self.tstart)
k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [5, 2], [6], [8], [9]] * 5
N = 100000
print len(k)
with Timer('set'):
for i in xrange(N):
kt = [tuple(i) for i in k]
skt = set(kt)
kk = [list(i) for i in skt]
with Timer('sort'):
for i in xrange(N):
ks = sorted(k)
dedup = [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]
with Timer('groupby'):
for i in xrange(N):
k = sorted(k)
dedup = list(k for k, _ in itertools.groupby(k))
with Timer('loop in'):
for i in xrange(N):
new_k = []
for elem in k:
if elem not in new_k:
new_k.append(elem)
"loop in" (metode kuadrat) tercepat dari semua untuk daftar pendek. Untuk daftar panjang, lebih cepat daripada semua orang kecuali metode groupby. Apakah ini masuk akal?
Untuk daftar singkat (yang ada di kode), 100000 iterasi:
[set] Elapsed: 1.3900001049
[sort] Elapsed: 0.891000032425
[groupby] Elapsed: 0.780999898911
[loop in] Elapsed: 0.578000068665
Untuk daftar yang lebih panjang (yang ada di kode digandakan 5 kali):
[set] Elapsed: 3.68700003624
[sort] Elapsed: 3.43799996376
[groupby] Elapsed: 1.03099989891
[loop in] Elapsed: 1.85900020599
Jawaban:
itertools
sering menawarkan solusi tercepat dan paling ampuh untuk masalah seperti ini, dan juga layak mendapatkan akrab dengan -!)Sunting : seperti yang saya sebutkan dalam komentar, upaya pengoptimalan normal difokuskan pada input besar (pendekatan O besar) karena jauh lebih mudah sehingga menawarkan pengembalian yang baik atas upaya. Tapi kadang-kadang (pada dasarnya untuk "kemacetan yang sangat penting" dalam loop dalam kode yang mendorong batas-batas batas kinerja) seseorang mungkin perlu menjelaskan lebih detail, menyediakan distribusi probabilitas, memutuskan ukuran kinerja mana yang akan dioptimalkan (mungkin batas atas atau persentil ke-90 lebih penting daripada rata-rata atau median, bergantung pada aplikasinya), melakukan pemeriksaan kemungkinan heuristik di awal untuk memilih algoritme yang berbeda bergantung pada karakteristik data masukan, dan seterusnya.
Pengukuran yang cermat dari kinerja "titik" (kode A vs kode B untuk input tertentu) adalah bagian dari proses yang sangat mahal ini, dan modul pustaka standar
timeit
membantu di sini. Namun, lebih mudah menggunakannya pada prompt shell. Misalnya, berikut adalah modul singkat untuk menunjukkan pendekatan umum untuk masalah ini, simpan sebagainodup.py
:Perhatikan pemeriksaan kewarasan (dilakukan saat Anda baru saja melakukannya
python nodup.py
) dan teknik pengangkatan dasar (buat nama global konstan menjadi lokal untuk setiap fungsi untuk kecepatan) untuk menempatkan segala sesuatunya pada pijakan yang sama.Sekarang kita dapat menjalankan pemeriksaan pada daftar contoh kecil:
mengkonfirmasikan bahwa pendekatan kuadrat memiliki konstanta yang cukup kecil untuk membuatnya menarik untuk daftar kecil dengan sedikit nilai duplikat. Dengan daftar singkat tanpa duplikat:
pendekatan kuadrat tidak buruk, tetapi jenis dan kelompok yang lebih baik. Dll, dll.
Jika (seperti yang ditunjukkan oleh obsesi dengan kinerja) operasi ini berada pada inti lingkaran dalam dari aplikasi pendorong-batas Anda, ada baiknya mencoba rangkaian pengujian yang sama pada sampel masukan perwakilan lainnya, mungkin mendeteksi beberapa ukuran sederhana yang secara heuristik dapat memungkinkan Anda pilih satu atau pendekatan lain (tetapi ukurannya harus cepat, tentu saja).
Ini juga layak dipertimbangkan untuk mempertahankan representasi yang berbeda untuk
k
- mengapa itu harus berupa daftar daftar daripada satu set tupel di tempat pertama? Jika tugas penghapusan duplikat sering terjadi, dan pembuatan profil menunjukkannya sebagai penghambat kinerja program, menyimpan sekumpulan tupel sepanjang waktu dan mendapatkan daftar daftar darinya hanya jika dan jika diperlukan, mungkin lebih cepat secara keseluruhan, misalnya.sumber
Melakukannya secara manual, membuat
k
daftar baru dan menambahkan entri yang sejauh ini tidak ditemukan:Mudah dipahami, dan Anda mempertahankan urutan kemunculan pertama setiap elemen semestinya itu berguna, tapi saya rasa itu kuadrat dalam kompleksitas saat Anda mencari keseluruhan
new_k
untuk setiap elemen.sumber
k = ([[1, 2], [4], [5, 6, 2], [1, 2], [3], [5, 2], [6], [8], [9]] +[[x] for x in range(1000)]) *5
akan menunjukkan perilaku kuadrat dengan baikSaya tidak tahu apakah itu pasti lebih cepat, tetapi Anda tidak harus menggunakan tupel dan set.
sumber
random
, dan atur waktunyatime
.Semua
set
solusi terkait untuk masalah ini sejauh ini memerlukan pembuatan keseluruhanset
sebelum iterasi.Hal ini dimungkinkan untuk membuat ini malas, dan pada saat yang sama mempertahankan ketertiban, dengan mengulang daftar daftar dan menambahkan ke "seen"
set
. Kemudian hanya menghasilkan daftar jika tidak ditemukan di pelacak iniset
.Ini
unique_everseen
resep tersedia diitertools
docs . Ini juga tersedia ditoolz
perpustakaan pihak ketiga :Perhatikan bahwa
tuple
konversi diperlukan karena daftar tidak dapat dicirikan.sumber
Bahkan daftar "panjang" Anda cukup pendek. Juga, apakah Anda memilihnya untuk dicocokkan dengan data sebenarnya? Performa akan berbeda dengan tampilan data yang sebenarnya. Misalnya, Anda memiliki daftar pendek yang berulang-ulang untuk membuat daftar yang lebih panjang. Ini berarti bahwa solusi kuadrat dalam tolok ukur Anda adalah linier, tetapi tidak dalam kenyataannya.
Untuk daftar yang benar-benar besar, set kode adalah taruhan terbaik Anda — ini linier (meskipun haus ruang). Metode sort dan groupby adalah O (n log n) dan metode loop in jelas kuadrat, jadi Anda tahu bagaimana ini akan diskalakan saat n menjadi sangat besar. Jika ini adalah ukuran sebenarnya dari data yang Anda analisis, lalu siapa yang peduli? Itu kecil.
Kebetulan, saya melihat percepatan yang nyata jika saya tidak membentuk daftar perantara untuk membuat set, artinya jika saya mengganti
dengan
Solusi sebenarnya mungkin bergantung pada lebih banyak informasi: Apakah Anda yakin bahwa daftar daftar benar-benar merupakan representasi yang Anda butuhkan?
sumber
Daftar tupel dan {} bisa digunakan untuk menghapus duplikat
sumber
Buat kamus dengan tupel sebagai kuncinya, dan cetak kuncinya.
sumber
Ini seharusnya berhasil.
sumber
Anehnya, jawaban di atas menghapus 'duplikat' tetapi bagaimana jika saya juga ingin menghapus nilai duplikat ?? Berikut ini akan berguna dan tidak membuat objek baru di memori!
dan output daya adalah:
sumber
Solusi lain yang mungkin lebih umum dan sederhana adalah membuat kamus yang dikunci oleh versi string dari objek dan mendapatkan nilai () di akhir:
Masalahnya adalah ini hanya berfungsi untuk objek yang representasi stringnya merupakan kunci unik yang cukup baik (yang berlaku untuk sebagian besar objek native).
sumber