Ada built-in yang menghilangkan duplikat dari daftar di Python, sementara menjaga ketertiban? Saya tahu bahwa saya bisa menggunakan satu set untuk menghapus duplikat, tapi yang menghancurkan urutan asli. Saya juga tahu bahwa saya dapat menggulung saya sendiri seperti ini:
def uniq(input):
output = []
for x in input:
if x not in output:
output.append(x)
return output
(Terima kasih kepada penginapan bahwa kode sampel.)
Tapi aku'd ingin memanfaatkan diri dari built-in atau yang lebih Pythonic idiom jika mungkin.
Terkait pertanyaan: Python, apa algoritma tercepat untuk menghapus duplikat dari daftar, sehingga semua elemen yang unik sambil menjaga ketertiban?
Di sini anda memiliki beberapa alternatif: http://www.peterbe.com/plog/uniqifiers-benchmark
Tercepat:
def f7(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
Mengapa menetapkan dilihat.tambahkan
untuk seen_add
bukan hanya memanggil dilihat.tambahkan
? Python adalah bahasa dinamis, dan menyelesaikan dilihat.tambahkan
setiap iterasi lebih mahal daripada menyelesaikan variabel lokal. dilihat.tambahkan
yang bisa berubah antara iterasi, dan runtime isn't cukup pintar untuk mengesampingkan itu. Untuk bermain aman, hal ini untuk memeriksa objek setiap waktu.
Jika anda berencana untuk menggunakan fungsi ini banyak pada dataset yang sama, mungkin anda akan lebih baik dengan memerintahkan set: http://code.activestate.com/recipes/528878/
O(1) penyisipan, penghapusan dan anggota-cek per operasi.
(Kecil catatan tambahan: dilihat.add()
selalu kembali None
, sehingga atau
di atas adalah tidak hanya sebagai cara untuk mencoba menetapkan pembaruan, dan bukan sebagai bagian integral dari uji logis.)
Edit 2016
Sebagai Raymond menunjukkan, di python 3.5+ dimana OrderedDict
ini diimplementasikan dalam C, daftar pemahaman pendekatan akan lebih lambat dari OrderedDict
(kecuali jika anda benar-benar perlu daftar di akhir - dan bahkan kemudian, hanya jika masukan yang sangat pendek). Jadi solusi terbaik untuk 3.5+ adalah OrderedDict
.
Penting Mengedit 2015
Sebagai @abarnert catatan, more_itertools
perpustakaan (pip menginstal more_itertools
) berisi unique_everseen
fungsi yang dibangun untuk memecahkan masalah ini tanpa ada tidak terbaca (tidak terlihat.tambahkan
) mutasi di daftar pemahaman. Ini juga merupakan solusi tercepat terlalu:
>>> from more_itertools import unique_everseen
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(unique_everseen(items))
[1, 2, 0, 3]
Hanya satu perpustakaan sederhana impor dan tidak ada hacks.
Ini berasal dari sebuah implementasi dari itertools resep unique_everseen
yang tampak seperti:
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
Di Python 2.7+
<menyerang>diterima umum idiom</strike> (yang bekerja tetapi isn't dioptimalkan untuk kecepatan, sekarang saya akan menggunakan unique_everseen
) untuk menggunakan collections.OrderedDict
:
Runtime: O(N)
>>> from collections import OrderedDict
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]
Ini terlihat jauh lebih baik dari:
seen = set()
[x for x in seq if x not in seen and not seen.add(x)]
dan doesn't memanfaatkan jelek hack:
not seen.add(x)
yang bergantung pada fakta bahwa set.tambahkan
adalah di-tempat metode yang selalu kembali Tidak
jadi tidak Ada
mengevaluasi Benar
.
Namun perlu dicatat bahwa hack solusi yang lebih cepat di baku, kecepatan angin dan meskipun ia memiliki runtime kompleksitas O(N).
Di Python 2.7, cara baru menghapus duplikat dari iterable sambil menjaga dalam urutan asli adalah:
>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
Di Python 3.5, yang OrderedDict memiliki implementasi C. Saya timing menunjukkan bahwa sekarang ini adalah investasi tercepat dan terpendek dari berbagai pendekatan untuk Python 3.5.
Di Python 3.6, biasa dict menjadi terurut dan kompak. (Fitur ini berlaku untuk CPython dan Mount tapi mungkin tidak hadir dalam implementasi lainnya). Yang memberi kita baru cara tercepat deduping sementara tetap mempertahankan urutan:
>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
Di Python 3.7, biasa dict dijamin untuk kedua memerintahkan seluruh implementasi. Jadi, terpendek dan tercepat solusinya adalah:
>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
Respon ke @max: Setelah anda pindah ke 3.6 3.7 dan gunakan secara teratur dict bukan OrderedDict, anda dapat't benar-benar mengalahkan kinerja dengan cara lain. Kamus padat dan mudah mengkonversi ke daftar dengan hampir tidak ada biaya overhead. Daftar target pra-ukuran untuk len(d) yang menyimpan semua mengubah ukuran yang terjadi dalam daftar pemahaman. Juga, karena kunci internal daftar padat, menyalin pointer adalah sekitar hampir secepat daftar copy.
Bukan untuk menendang kuda mati (pertanyaan ini sangat lama dan sudah memiliki banyak jawaban yang baik), tetapi di sini adalah sebuah solusi menggunakan panda yang cukup cepat dalam banyak keadaan dan mati mudah digunakan.
import pandas as pd
my_list = [0, 1, 2, 3, 4, 1, 2, 3, 5]
>>> pd.Series(my_list).drop_duplicates().tolist()
# Output:
# [0, 1, 2, 3, 4, 5]
from itertools import groupby
[ key for key,_ in groupby(sortedList)]
Daftar doesn't bahkan harus diurutkan*, dengan kondisi yang cukup adalah bahwa nilai-nilai yang sama yang dikelompokkan bersama-sama.
Edit: saya berasumsi bahwa "menjaga ketertiban" berarti bahwa daftar ini benar-benar memerintahkan. Jika hal ini tidak terjadi, maka solusi dari MizardX adalah salah satu yang tepat.
Edit: Ini namun cara yang paling elegan untuk "kompres duplikat berturut-elemen menjadi satu elemen".
Saya pikir jika anda ingin menjaga ketertiban,
list1 = ['b','c','d','b','c','a','a']
list2 = list(set(list1))
list2.sort(key=list1.index)
print list2
list1 = ['b','c','d','b','c','a','a']
list2 = sorted(set(list1),key=list1.index)
print list2
list1 = ['b','c','d','b','c','a','a']
list2 = []
for i in list1:
if not i in list2:
list2.append(i)`
print list2
list1 = ['b','c','d','b','c','a','a']
list2 = []
[list2.append(i) for i in list1 if not i in list2]
print list2
Di Python 3.7 dan di atas, kamus dijamin untuk mengingat kunci penyisipan order. Jawaban untuk ini pertanyaan yang merangkum keadaan sekarang.
The OrderedDict
solusi sehingga menjadi usang dan tanpa impor setiap pernyataan yang kita dapat hanya masalah:
>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> list(dict.fromkeys(lst))
[1, 2, 3, 4]
Hanya untuk menambahkan yang lain (sangat performant) pelaksanaan seperti fungsi dari modul eksternal1: iteration_utilities.unique_everseen
:
>>> from iteration_utilities import unique_everseen
>>> lst = [1,1,1,2,3,2,2,2,1,3,4]
>>> list(unique_everseen(lst))
[1, 2, 3, 4]
Saya melakukan beberapa timing (Python 3.6) dan ini menunjukkan bahwa itu's lebih cepat dari semua alternatif lain saya diuji, termasuk OrderedDict.fromkeys
, f7
dan more_itertools.unique_everseen
:
%matplotlib notebook
from iteration_utilities import unique_everseen
from collections import OrderedDict
from more_itertools import unique_everseen as mi_unique_everseen
def f7(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
def iteration_utilities_unique_everseen(seq):
return list(unique_everseen(seq))
def more_itertools_unique_everseen(seq):
return list(mi_unique_everseen(seq))
def odict(seq):
return list(OrderedDict.fromkeys(seq))
from simple_benchmark import benchmark
b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
{2**i: list(range(2**i)) for i in range(1, 20)},
'list size (no duplicates)')
b.plot()
Dan hanya untuk memastikan saya juga melakukan tes dengan lebih duplikat hanya untuk memeriksa apakah itu membuat perbedaan:
import random
b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
{2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(1, 20)},
'list size (lots of duplicates)')
b.plot()
Dan salah satu yang hanya mengandung satu nilai:
b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
{2**i: [1]*(2**i) for i in range(1, 20)},
'list size (only duplicates)')
b.plot()
Dalam semua kasus ini iteration_utilities.unique_everseen
fungsi adalah yang tercepat (di komputer saya).
Ini iteration_utilities.unique_everseen
fungsi juga dapat menangani unhashable nilai-nilai yang di input (namun dengan sebuah O(n*n)
kinerja bukan O(n)
kinerja ketika nilai-nilai yang hashable).
>>> lst = [{1}, {1}, {2}, {1}, {3}]
>>> list(unique_everseen(lst))
[{1}, {2}, {3}]
1 Disclaimer: saya'm penulis dari paket itu.
Untuk yang lain sangat terlambat jawaban lain yang sangat lama pertanyaan:
The itertools
resep memiliki fungsi yang melakukan hal ini, menggunakan melihat
set teknik, tetapi:
kunci
fungsi.dilihat.tambahkan
bukan melihat itu sampai N kali. (f7
juga melakukan hal ini, tetapi beberapa versi don't.)ifilterfalse
, sehingga anda hanya perlu loop atas unsur-unsur yang unik dalam Python, bukan semua dari mereka. (Anda masih iterate melalui semua dari mereka dalam ifilterfalse
, tentu saja, tapi itu's di C, dan jauh lebih cepat.)Itu benar-benar lebih cepat dari f7
? Hal ini tergantung pada data anda, sehingga anda'll harus menguji dan melihat. Jika anda ingin daftar di akhir, f7
menggunakan listcomp, dan ada's tidak ada cara untuk melakukan itu di sini. (Anda dapat langsung menambahkan
bukan hasil a'ing, atau anda dapat memberi makan generator ke dalam
daftar` fungsi, tapi tak satu pun dapat secepat LIST_APPEND dalam listcomp.) Pada setiap tingkat, biasanya, memeras keluar beberapa mikrodetik tidak akan menjadi penting bisa mudah dimengerti, dapat digunakan kembali, sudah ditulis dengan fungsi yang doesn't membutuhkan DSU bila anda ingin menghias.
Seperti dengan semua resep,'s juga tersedia di lebih-iterools
.
Jika anda hanya ingin tidak adakunci
kasus, anda dapat menyederhanakan hal seperti:
def unique(iterable):
seen = set()
seen_add = seen.add
for element in itertools.ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
Pinjaman rekursif ide yang digunakan dalam definining Haskell's inti
fungsi untuk daftar, ini akan menjadi pendekatan rekursif:
def unique(lst):
return [] if lst==[] else [lst[0]] + unique(filter(lambda x: x!= lst[0], lst[1:]))
misalnya:
In [118]: unique([1,5,1,1,4,3,4])
Out[118]: [1, 5, 4, 3]
Aku mencoba untuk tumbuh ukuran data dan melihat sub-linear time-kompleksitas (tidak definitif, tetapi menunjukkan ini harus baik untuk data normal).
In [122]: %timeit unique(np.random.randint(5, size=(1)))
10000 loops, best of 3: 25.3 us per loop
In [123]: %timeit unique(np.random.randint(5, size=(10)))
10000 loops, best of 3: 42.9 us per loop
In [124]: %timeit unique(np.random.randint(5, size=(100)))
10000 loops, best of 3: 132 us per loop
In [125]: %timeit unique(np.random.randint(5, size=(1000)))
1000 loops, best of 3: 1.05 ms per loop
In [126]: %timeit unique(np.random.randint(5, size=(10000)))
100 loops, best of 3: 11 ms per loop
Saya juga berpikir itu's menarik ini bisa dengan mudah digeneralisasi untuk keunikan dengan operasi lainnya. Seperti ini:
import operator
def unique(lst, cmp_op=operator.ne):
return [] if lst==[] else [lst[0]] + unique(filter(lambda x: cmp_op(x, lst[0]), lst[1:]), cmp_op)
Misalnya, anda bisa lulus dalam fungsi yang menggunakan konsep pembulatan yang sama integer seolah-olah itu adalah "kesetaraan" untuk keunikan keperluan, seperti ini:
def test_round(x,y):
return round(x) != round(y)
kemudian yang unik(some_list, test_round) akan memberikan unsur unik dari daftar di mana keunikan tidak lagi dimaksudkan tradisional kesetaraan (yang tersirat dengan menggunakan apapun yang ditetapkan atau berbasis dict-kunci berbasis pendekatan untuk masalah ini), tetapi bukan dimaksudkan untuk mengambil hanya elemen pertama yang putaran ke K untuk masing-masing kemungkinan bilangan bulat K sehingga unsur-unsur yang mungkin bulat, misalnya:
In [6]: unique([1.2, 5, 1.9, 1.1, 4.2, 3, 4.8], test_round)
Out[6]: [1.2, 5, 1.9, 4.2, 3]
5 x lebih cepat mengurangi varian tapi lebih canggih
>>> l = [5, 6, 6, 1, 1, 2, 2, 3, 4]
>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]
Penjelasan:
default = (list(), set())
# use list to keep order
# use set to make lookup faster
def reducer(result, item):
if item not in result[1]:
result[0].append(item)
result[1].add(item)
return result
>>> reduce(reducer, l, default)[0]
[5, 6, 1, 2, 3, 4]
Anda dapat referensi daftar pemahaman seperti yang sedang dibangun oleh simbol '_[1]'.
sebagai contoh, berikut fungsi unik-ifies daftar dari elemen-elemen tanpa mengubah urutan mereka dengan referensi daftar pemahaman.
def unique(my_list):
return [x for x in my_list if x not in locals()['_[1]']]
Demo:
l1 = [1, 2, 3, 4, 1, 2, 3, 4, 5]
l2 = [x for x in l1 if x not in locals()['_[1]']]
print l2
Output:
[1, 2, 3, 4, 5]
Solusi tanpa menggunakan modul impor atau set:
text = "ask not what your country can do for you ask what you can do for your country"
sentence = text.split(" ")
noduplicates = [(sentence[i]) for i in range (0,len(sentence)) if sentence[i] not in sentence[:i]]
print(noduplicates)
Memberikan output:
['ask', 'not', 'what', 'your', 'country', 'can', 'do', 'for', 'you']