Видалення дублікатів у списках


995

Мені дуже потрібно написати програму, щоб перевірити, чи є в списку дублікати, а якщо він є, він видаляє їх і повертає новий список з елементами, які не дублювалися / видалялися. Це те, що я маю, але якщо чесно, я не знаю, що робити.

def remove_duplicates():
    t = ['a', 'b', 'c', 'd']
    t2 = ['a', 'c', 'd']
    for t in t2:
        t.append(t.remove())
    return t

22
У вашому описі зазначено, що ви перевіряєте "список" на дублікати, але ваш код перевіряє два списки.
Брендан Лонг


* за допомогою set: list (set (ELEMENTS_LIST)) * за допомогою словника: list (dict.fromkeys (ELEMENTS_LIST))
Шаян Амані

Відповіді:


1640

Загальним підходом до отримання унікальної колекції предметів є використання set. Набори - це не упорядковані колекції різних предметів. Щоб створити набір з будь-якого ітерабельного, ви можете просто передати його до вбудованої set()функції. Якщо пізніше вам знову потрібен реальний список, ви можете аналогічно передати набір list()функції.

Наступний приклад повинен охоплювати все, що ви намагаєтеся зробити:

>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> list(set(t))
[1, 2, 3, 5, 6, 7, 8]
>>> s = [1, 2, 3]
>>> list(set(t) - set(s))
[8, 5, 6, 7]

Як видно з результату прикладу, вихідний порядок не підтримується . Як було сказано вище, самі набори - це не упорядковані колекції, тому порядок втрачається. При перетворенні набору назад у список створюється довільний порядок.

Підтримання порядку

Якщо для вас важливий порядок, тоді вам доведеться використовувати інший механізм. Дуже поширене рішення для цього - покладатися на OrderedDictдотримання порядку клавіш під час вставки:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Починаючи з Python 3.7 , вбудований словник гарантовано також підтримує порядок вставки, тому ви можете також використовувати це безпосередньо, якщо ви перебуваєте на Python 3.7 або пізнішої версії (або CPython 3.6):

>>> list(dict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Зауважте, що це може мати певні наплати для створення словника, а потім створення списку з нього. Якщо вам не потрібно зберігати замовлення, вам часто краще використовувати набір, тим більше, що це дає вам набагато більше операцій, з якими можна працювати. Ознайомтеся з цим питанням, щоб отримати докладнішу інформацію та альтернативні способи збереження порядку при видаленні дублікатів.


Нарешті зауважте, що і рішення set, і рішення OrderedDict/ dictвимагають, щоб ваші предмети були зручними . Зазвичай це означає, що вони повинні бути незмінні. Якщо вам доведеться мати справу з предметами, які не підлягають перегляду (наприклад, об'єкти списку), тоді вам доведеться використовувати повільний підхід, в якому в основному вам доведеться порівнювати кожен елемент з кожним іншим елементом в вкладеному циклі.


4
Це не працює для незмінних елементів списку (наприклад, список списків)
KNejad

3
@KNejad Саме так йде останній абзац.
ткнути

Ой, ой. Треба було прочитати всю справу. Що я в кінцевому підсумку робив - використовував кортежі замість списків, щоб такий підхід міг працювати.
KNejad

додайте це до прикладу, t = [3, 2, 1, 1, 2, 5, 6, 7, 8], чітко показує різницю!
sailfish009

"... накладні витрати на створення словника спочатку ... Якщо вам фактично не потрібно зберігати замовлення, вам краще скористатися набором." - Я це профілював, бо мені було цікаво, чи це насправді правда. Мої таймінги показують, що дійсно набір дещо швидший: 1,12 мкс на цикл (набір) проти 1,53 мкс на цикл (диктат) за 1 М циклу з абсолютною різницею в часі близько 4 секунд за 1 М ітерацій. Тож якщо ви робите це в тісному внутрішньому циклі, то, можливо, все одно, можливо, це не так.
міллердев

414

У Python 2.7 новий спосіб вилучення дублікатів з ітерабельного файлу, зберігаючи його в початковому порядку:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

У Python 3.5 OrdersDict має C-реалізацію. Мої таймінги показують, що зараз це найшвидший і найкоротший з різних підходів для Python 3.5.

У Python 3.6 регулярний диктат став одночасно упорядкованим та компактним. (Ця функція утримується для CPython та PyPy, але може не бути в інших реалізаціях). Це дає нам новий найшвидший спосіб виведення, зберігаючи порядок:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

У Python 3.7 регулярний диктант гарантується обом упорядкованим у всіх реалізаціях. Отже, найкоротше і швидке рішення:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

10
Я думаю, що це єдиний спосіб навести предмети в порядку.
Герберт Амарал


5
@MartijnPieters Виправлення: Я думаю, що це єдиний простий спосіб підтримувати елементи в порядку.
Герберт Амарал

11
Для цього теж зміст вихідного списку повинен бути hashable
Давіде

Як зазначалося @Davide, оригінальний список повинен бути змінним. Це означає, що це не працює для списку словників. TypeError: unhashable type: 'dictlist'
CraZ

186

Це однолінійний: list(set(source_list))зробить трюк.

A set- це те, що не може мати копії.

Оновлення: підхід із збереження замовлення - це два рядки:

from collections import OrderedDict
OrderedDict((x, True) for x in source_list).keys()

Тут ми використовуємо той факт, що OrderedDictзапам'ятовує порядок вставки ключів, а не змінює його при оновленні значення певного ключа. Ми вставляємо Trueяк значення, але ми могли б вставити що завгодно, значення просто не використовуються. ( setпрацює дуже добре, як і dictз ігнорованими значеннями.)


4
Це працює лише, якщо source_listє хешируемим.
Адріан

@AdrianKeister: Це правда. Є об'єкти, які мають розумну семантику рівності, але не підлягають доступності, наприклад, списки. Якщо ми не можемо мати ярлик на зразок hastable, ми закінчуємо квадратичний алгоритм просто порівняння кожного елемента з усіма відомими в даний час унікальними елементами. Це може бути цілком нормально для коротких входів, особливо з великою кількістю дублікатів.
9000

Правильно, точно. Я думаю, що ваша відповідь була б більш якісною, якби ви врахували цей дуже поширений випадок використання.
Адріан

94
>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> s = []
>>> for i in t:
       if i not in s:
          s.append(i)
>>> s
[1, 2, 3, 5, 6, 7, 8]

33
Зауважимо, що цей метод працює в O (n ^ 2) час і тому дуже великий у великих списках.
dotancohen

@Chris_Rands: Не впевнений, що frozensetпрацює з вмістом, який не доступний для доступу. Я все ще отримую помилку, що не доступна для використання frozenset.
Адріан

85

Якщо ви не дбаєте про замовлення, просто зробіть це:

def remove_duplicates(l):
    return list(set(l))

А setгарантовано не буде дублікатів.


3
Не працює, якщо не lє хешируемой.
Адріан

41

Скласти новий список із збереженням порядку перших елементів дублікатів у L

newlist=[ii for n,ii in enumerate(L) if ii not in L[:n]]

наприклад, if L=[1, 2, 2, 3, 4, 2, 4, 3, 5]тоді newlistбуде[1,2,3,4,5]

Це перевіряє, що кожен новий елемент раніше не відображався у списку перед його додаванням. Також він не потребує імпорту.


3
Це має часову складність O (n ^ 2) . Відповіді з setі OrderedDictможуть мати меншу амортизовану часову складність.
blubberdiblub

Я використовував у своєму коді це рішення і чудово працював, але думаю, що це забирає багато часу
Герасим Рагаваніс,

@blubberdiblub Ви можете пояснити, який ще механізм ефективної роботи коду існує у наборі та OrрадDict, що може зробити їх менш трудомісткими? (без урахування накладних витрат на їх завантаження)
ilias iliadis

@iliasiliadis У звичайних реалізаціях множини та dict використовують хеші або (деякі форми збалансованих) дерев. Ви повинні розглянути можливість створення множини чи диктату та пошуку в ньому (кілька разів), але їх амортизована складність, як правило, все ще нижча, ніж O (n ^ 2) . "Амортизовані" простими словами означають в середньому (вони можуть мати найгірші випадки з більшою складністю, ніж середній випадок). Це актуально лише тоді, коли у вас є велика кількість предметів.
blubberdiblub

25

Сьогодні колега надіслав прийняту відповідь як частину свого коду для перегляду коду. Хоча я, безумовно, захоплююсь елегантністю відповіді, про яку я питаю, я не задоволений виконанням. Я спробував це рішення (я використовую набір, щоб скоротити час пошуку)

def ordered_set(in_list):
    out_list = []
    added = set()
    for val in in_list:
        if not val in added:
            out_list.append(val)
            added.add(val)
    return out_list

Для порівняння ефективності я використав випадкову вибірку з 100 цілих чисел - 62 були унікальними

from random import randint
x = [randint(0,100) for _ in xrange(100)]

In [131]: len(set(x))
Out[131]: 62

Ось результати вимірювань

In [129]: %timeit list(OrderedDict.fromkeys(x))
10000 loops, best of 3: 86.4 us per loop

In [130]: %timeit ordered_set(x)
100000 loops, best of 3: 15.1 us per loop

Ну, що станеться, якщо набір вилучити з рішення?

def ordered_set(inlist):
    out_list = []
    for val in inlist:
        if not val in out_list:
            out_list.append(val)
    return out_list

Результат не такий вже й поганий, як у OrdersDict , але все-таки більше ніж у 3 рази від оригінального рішення

In [136]: %timeit ordered_set(x)
10000 loops, best of 3: 52.6 us per loop

Приємно використовувати швидкий пошук встановити для прискорення циклічного порівняння. Якщо замовлення не має значення, список (set (x)) все ще на 6 разів швидший за цей
Joop

@Joop, це було моє перше питання для мого колеги - порядок має значення; інакше це було б тривіальним питанням
вулкан

оптимізована версія замовленого набору для всіх, хто цікавиться def unique(iterable)::; seen = set(); seen_add = seen.add; return [item for item in iterable if not item in seen and not seen_add(item)]
Др.

25

Також є рішення, що використовують Pandas і Numpy. Вони обидва повертають нумерований масив, тому вам доведеться скористатися функцією, .tolist()якщо потрібно список.

t=['a','a','b','b','b','c','c','c']
t2= ['c','c','b','b','b','a','a','a']

Розчин панди

Використання функції Pandas unique():

import pandas as pd
pd.unique(t).tolist()
>>>['a','b','c']
pd.unique(t2).tolist()
>>>['c','b','a']

Річковий розчин

Використання функції numpy unique().

import numpy as np
np.unique(t).tolist()
>>>['a','b','c']
np.unique(t2).tolist()
>>>['a','b','c']

Зауважте, що numpy.unique () також сортує значення . Отже список t2повертається відсортованим. Якщо ви хочете, щоб замовлення зберігалося, використовуйте, як у цій відповіді :

_, idx = np.unique(t2, return_index=True)
t2[np.sort(idx)].tolist()
>>>['c','b','a']

Рішення не настільки елегантне порівняно з іншими, проте порівняно з pandas.unique (), numpy.unique () дозволяє також перевірити, чи є вкладені масиви унікальними вздовж однієї вибраної осі.


Це перетворить список в numpy масив, який є безладом і не буде працювати для рядків.
користувач227666

1
@ user227666 дякую за Ваш відгук, але це неправда, він працює навіть із рядком, і ви можете додати .tolist, якщо хочете отримати список ...
GM

1
Я думаю, це щось на зразок спроби вбити бджолу кувалдою. Працює, впевнено! Але імпорт бібліотеки саме для цієї мети може бути трохи зайвим, чи не так?
Дебосміт Рей

@DebosmitRay це може бути корисно, якщо ви працюєте в Data Science, де зазвичай ви працюєте з numpy і багато разів вам потрібно працювати з numpy масивом.
ГМ

найкраща відповідь у 2020 році @DebosmitRay Я сподіваюся, що ви передумаєте та використовуєте numpy / pandas кожного разу, коли зможете
Egos

21

Ще один спосіб:

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]

1
Зауважте, що в сучасних версіях Python (я думаю, що 2.7+, але я не пам'ятаю точно), keys()повертає об’єкт подання словника, а не список.
Дастін Віатт

16

Просто і легко:

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanlist = []
[cleanlist.append(x) for x in myList if x not in cleanlist]

Вихід:

>>> cleanlist 
[1, 2, 3, 5, 6, 7, 8]

5
квадратична складність, тим не менше - inце операція O (n), і у вас cleanlistбуде щонайбільше nцифр => найгірший випадок ~ O (n ^ 2)
jermenkoo

6
Ознайомлення зі списком не слід використовувати для побічних ефектів.
Жан-Франсуа Фабре

13

У цій відповіді буде два розділи: Два унікальних рішення та графік швидкості для конкретних рішень.

Видалення повторюваних елементів

Більшість з цих відповідей видаляють лише дублікати елементів, які є хешируемими , але це питання не означає, що він не просто потребує хешируемих елементів, тобто я запропоную деякі рішення, які не потребують перебірливих елементів.

collection.Counter - це потужний інструмент у стандартній бібліотеці, який може бути ідеальним для цього. Є лише одне інше рішення, яке навіть має лічильник у ньому. Однак це рішення також обмежується доступними ключами.

Щоб дозволити незмінні ключі в Counter, я створив клас Container, який намагатиметься отримати хеш-функцію об'єкта за замовчуванням, але якщо він не вдасться, він спробує свою функцію ідентичності. Він також визначає еквівалентний і хеш- метод. Цього має бути достатньо, щоб дозволити невмирущі предмети в нашому рішенні. Незміцнілі предмети будуть розглядатися так, ніби вони підлягають перегляду. Однак ця хеш-функція використовує ідентичність для нездатних об'єктів, тобто два рівних об'єкти, які є нерозбірливими, не працюватимуть. Я пропоную вам переосмислити це та змінити його, щоб використовувати хеш еквівалентного типу, що змінюється (наприклад, hash(tuple(my_list))якщо my_listсписок є).

Я також прийняв два рішення. Ще одне рішення, яке підтримує порядок елементів, використовуючи підклас OrdersDict і Counter, який має назву "OrdersCounter". Тепер ось функції:

from collections import OrderedDict, Counter

class Container:
    def __init__(self, obj):
        self.obj = obj
    def __eq__(self, obj):
        return self.obj == obj
    def __hash__(self):
        try:
            return hash(self.obj)
        except:
            return id(self.obj)

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

def remd(sequence):
    cnt = Counter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

def oremd(sequence):
    cnt = OrderedCounter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

remd - це не упорядковане сортування, oremd - упорядковане сортування. Ви можете чітко сказати, хто з них швидший, але я все-таки поясню. Не упорядковане сортування трохи швидше. Він зберігає менше даних, оскільки не потребує порядку.

Тепер я також хотів показати порівняння швидкості кожної відповіді. Отже, я зараз це зроблю.

Яка функція найшвидша?

Для видалення дублікатів я зібрав 10 функцій із кількох відповідей. Я обчислював швидкість кожної функції і вкладав її у графік, використовуючи matplotlib.pyplot .

Я розділив це на три кола графіків. Хешируемость - це будь-який об'єкт, який можна хешировать, нерозбірливий - це будь-який об'єкт, який не можна хешировать. Впорядкована послідовність - це послідовність, яка зберігає порядок, не упорядкована послідовність не зберігає порядок. Тепер ось ще кілька термінів:

Не упорядкований Hashable був для будь-якого методу, який видаляв дублікати, які не обов'язково повинні були утримувати замовлення. Він не повинен був працювати на unhashables, але це міг.

Замовлений Hashable був для будь-якого методу, який підтримував порядок елементів у списку, але він не повинен працювати для unhashables, але він міг.

Замовлений Unhashable - це будь-який метод, який підтримував порядок позицій у списку та працював на unhashables.

На осі у - кількість секунд, які знадобилися.

На осі x - це число, до якого застосовувалася функція.

Ми створили послідовності для не упорядкованих хешбелів та замовили хешбелі з наступним розумінням: [list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]

Для замовлених unhashables: [[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]

Зауважте, що в діапазоні є "крок", тому що без цього це займе 10 разів. Також тому, що, на мою особисту думку, я думав, що це, можливо, виглядало трохи легше для читання.

Також зауважте, що клавіші легенди - це те, що я намагався здогадатися як найважливіші частини функції. Щодо того, яку функцію виконує найгірша чи найкраща? Графік говорить сам за себе.

З урахуванням цього, ось графіки.

Не упорядковані мішалки

введіть тут опис зображення (Збільшення масштабу) введіть тут опис зображення

Впорядковані гашені вироби

введіть тут опис зображення (Збільшення масштабу) введіть тут опис зображення

Замовлені Unhashables

введіть тут опис зображення (Збільшення масштабу) введіть тут опис зображення


11

У моєму списку був диктант, тому я не міг використати вищевказаний підхід. Я отримав помилку:

TypeError: unhashable type:

Тож якщо ви дбаєте про замовлення та / або деякі предмети, які не є непорушними . Тоді це може бути вам корисним:

def make_unique(original_list):
    unique_list = []
    [unique_list.append(obj) for obj in original_list if obj not in unique_list]
    return unique_list

Деякі можуть вважати розуміння списку з побічним ефектом не гарним рішенням. Ось альтернатива:

def make_unique(original_list):
    unique_list = []
    map(lambda x: unique_list.append(x) if (x not in unique_list) else False, original_list)
    return unique_list

6
mapз побічним ефектом є навіть більш оманливим, ніж списоккомплектів із побічним ефектом. Крім того, lambda x: unique_list.append(x)це просто незграбний і повільніший шлях проходження unique_list.append.
abarnert

Дуже корисний спосіб додавання елементів лише в одному рядку, дякую!
ЗЛНК

2
@ZLNK, будь ласка, ніколи цього не використовуй. Окрім того, що концептуально некрасивий, він також є надзвичайно неефективним, оскільки ви насправді створюєте потенційно великий список і викидаєте його просто для виконання базової ітерації.
Елі Корвіго

10

Усі підходи до збереження порядку, які я бачив тут до цього часу, використовують або наївне порівняння (в кращому випадку з (n ^ 2) часовою складністю), або комбінації з великою вагою OrderedDicts/ set+ list, які обмежуються доступними введеннями. Ось хеш-незалежне рішення O (nlogn):

Оновлення додало keyаргументи, документацію та сумісність Python 3.

# from functools import reduce <-- add this import on Python 3

def uniq(iterable, key=lambda x: x):
    """
    Remove duplicates from an iterable. Preserves order. 
    :type iterable: Iterable[Ord => A]
    :param iterable: an iterable of objects of any orderable type
    :type key: Callable[A] -> (Ord => B)
    :param key: optional argument; by default an item (A) is discarded 
    if another item (B), such that A == B, has already been encountered and taken. 
    If you provide a key, this condition changes to key(A) == key(B); the callable 
    must return orderable objects.
    """
    # Enumerate the list to restore order lately; reduce the sorted list; restore order
    def append_unique(acc, item):
        return acc if key(acc[-1][1]) == key(item[1]) else acc.append(item) or acc 
    srt_enum = sorted(enumerate(iterable), key=lambda item: key(item[1]))
    return [item[1] for item in sorted(reduce(append_unique, srt_enum, [srt_enum[0]]))] 

Однак для цього рішення потрібні впорядковані елементи. Я буду використовувати його, щоб уніфікувати свій список списків: боляче tuple()списки та хеширование їх. | | | | - Взагалі кажучи, хеш-процес займає час, пропорційний розміру цілих даних, тоді як це рішення займає час O (nlog (n)), залежно лише від довжини списку.
локсакс

Я думаю, що підхід на основі набору є настільки ж дешевим (O (n log n)), або дешевшим, ніж сортування + виявлення унікальних даних. (Хоча цей підхід набагато краще паралелізуватиметься.) Він також точно не зберігає початковий порядок, але він дає передбачуваний порядок.
9000

@ 9000 Це правда. Я ніколи не згадував про складність у часі підходу на основі хеш-таблиць, який, очевидно, є O (n). Тут ви можете знайти багато відповідей, що містять хеш-таблиці. Вони, однак, не є універсальними, оскільки вимагають об'єктів, які можуть бути зручними. Більше того, вони набагато більш об'ємні.
Елі Корвіго

Знадобиться час, щоб прочитати та зрозуміти цю відповідь. Чи є сенс перерахувати, коли ви не використовуєте індекси? The reduce() вже працює над відсортованою колекцією srt_enum, чому ви подали заявку sortedзнову?
Брайоні

@Brayoni перший сорт є для згрупування рівних значень, другий сорт - для відновлення початкового порядку. Перерахування потрібно для відстеження вихідного відносного порядку.
Елі Корвіго

9

Якщо ви хочете зберегти замовлення і не використовувати жодних зовнішніх модулів, тут це простий спосіб зробити це:

>>> t = [1, 9, 2, 3, 4, 5, 3, 6, 7, 5, 8, 9]
>>> list(dict.fromkeys(t))
[1, 9, 2, 3, 4, 5, 6, 7, 8]

Примітка. Цей метод зберігає порядок появи, тому, як було показано вище, дев'ять прийде за одним, оскільки це було вперше. Це, однак, той самий результат, який ви отримаєте при виконанні

from collections import OrderedDict
ulist=list(OrderedDict.fromkeys(l))

але він набагато коротший і працює швидше.

Це працює, тому що кожен раз, коли fromkeysфункція намагається створити новий ключ, якщо значення вже існує, воно просто перезапише. Однак це взагалі не впливає на словник, оскільки fromkeysстворює словник, у якому всі ключі мають значення None, тому ефективно він усуває всі дублікати таким чином.


Також спробуйте тут
виноградники

8

Ви також можете зробити це:

>>> t = [1, 2, 3, 3, 2, 4, 5, 6]
>>> s = [x for i, x in enumerate(t) if i == t.index(x)]
>>> s
[1, 2, 3, 4, 5, 6]

Причина, що вище працює, полягає в тому, що indexметод повертає лише перший індекс елемента. Дублікати елементів мають більш високі показники. Зверніться сюди :

list.index (x [, start [, end]])
Повертає нульовий індекс у списку першого елемента, значення якого x. Підвищує ValueError, якщо такого елемента немає.


Це жахливо неефективно. list.index- це операція лінійного часу, що робить ваше рішення квадратичним.
Елі Корвіго

Ти правий. Але я також вважаю, що цілком очевидно, що рішення має на меті бути одним вкладишем, який зберігає порядок. Все інше вже є тут.
Атонал

7

Спробуйте використовувати набори:

import sets
t = sets.Set(['a', 'b', 'c', 'd'])
t1 = sets.Set(['a', 'b', 'c'])

print t | t1
print t - t1

7

Скоротити варіант із збереженням замовлення:

Припустимо, що у нас є список:

l = [5, 6, 6, 1, 1, 2, 2, 3, 4]

Скоротити варіант (неефективний):

>>> reduce(lambda r, v: v in r and r or r + [v], l, [])
[5, 6, 1, 2, 3, 4]

5 разів швидше, але більш досконало

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

Пояснення:

default = (list(), set())
# user 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]

7

Найкращим підходом до видалення дублікатів із списку є використання функції set () , доступної в python, знову перетворення цього набору в список

In [2]: some_list = ['a','a','v','v','v','c','c','d']
In [3]: list(set(some_list))
Out[3]: ['a', 'c', 'd', 'v']

@MeetZaveri рада!
Анураг Місра

Миттєвий пошук нових списків та наборів не є безкоштовним. Що станеться, якщо ми робимо це багато разів поспіль (тобто в дуже тісному циклі), а списки дуже малі?
Z4-ярус

6

Ви можете використовувати таку функцію:

def rem_dupes(dup_list): 
    yooneeks = [] 
    for elem in dup_list: 
        if elem not in yooneeks: 
            yooneeks.append(elem) 
    return yooneeks

Приклад :

my_list = ['this','is','a','list','with','dupicates','in', 'the', 'list']

Використання:

rem_dupes(my_list)

['це', 'є', 'a', 'список', 'з', 'дублікати', 'в', 'the']


5

Є багато інших відповідей, які пропонують різні способи зробити це, але всі вони є пакетними операціями, а деякі з них викидають оригінальне замовлення. Це може бути нормально залежно від того, що вам потрібно, але якщо ви хочете повторити значення у порядку першого екземпляра кожного значення, і ви хочете видалити дублікати на ходу проти всіх одночасно, ви можете використовувати цей генератор:

def uniqify(iterable):
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item

Це повертає генератор / ітератор, тому ви можете використовувати його в будь-якому місці, де ви можете використовувати ітератор.

for unique_item in uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]):
    print(unique_item, end=' ')

print()

Вихід:

1 2 3 4 5 6 7 8

Якщо ви хочете list, ви можете зробити це:

unique_list = list(uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]))

print(unique_list)

Вихід:

[1, 2, 3, 4, 5, 6, 7, 8]

seen = set(iterable); for item in seen: yield itemмайже напевно швидше. (Я не пробував цього конкретного випадку, але це було б моїм припущенням.)
dylnmc

2
@dylnmc, це пакетна операція, і вона також втрачає впорядкування. Моя відповідь була спеціально покликана бути на ходу та в порядку першого появи. :)
Cyphase

5

Без використання набору

data=[1, 2, 3, 1, 2, 5, 6, 7, 8]
uni_data=[]
for dat in data:
    if dat not in uni_data:
        uni_data.append(dat)

print(uni_data) 

5

Ви можете використовувати setдля видалення дублікатів:

mylist = list(set(mylist))

Але зауважте, результати будуть не упорядковані. Якщо це питання:

mylist.sort()

1
Ви можете просто зробити: mylist = відсортовано (список (набір (мій список)))
Erik Campobadal

5

Ще один кращий підхід може бути,

import pandas as pd

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanList = pd.Series(myList).drop_duplicates().tolist()
print(cleanList)

#> [1, 2, 3, 5, 6, 7, 8]

і порядок залишається збереженим.


Хоча це може бути добре, використання такої важкої бібліотеки, як панди для цієї мети, здається непосильним.
Glutexo

4

Цей піклується про замовлення без зайвих клопотів (OrderdDict та інші). Напевно, не самий піфонічний спосіб, ані найкоротший шлях, але хитрість:

def remove_duplicates(list):
    ''' Removes duplicate items from a list '''
    singles_list = []
    for element in list:
        if element not in singles_list:
            singles_list.append(element)
    return singles_list

1. Ніколи не слід затінювати вбудовані імена (принаймні, настільки ж важливі list); 2. Ваш метод масштабується вкрай погано: він квадратичний у кількості елементів у list.
Елі Корвіго

1. Правильно, але це був приклад; 2. Правильно, і саме тому я запропонував це. Усі розміщені тут рішення мають плюси і мінуси. Якась жертва простота чи порядок, мої жертвують масштабністю.
cgf

це алгоритм "Художник Шлейміля" ...
Z4-ярус

4

нижче код простий для видалення дубліката в списку

def remove_duplicates(x):
    a = []
    for i in x:
        if i not in a:
            a.append(i)
    return a

print remove_duplicates([1,2,2,3,3,4])

повертається [1,2,3,4]


2
Якщо ви не дбаєте про замовлення, то це займе значно більше часу. list(set(..))(понад 1 мільйон пропусків) обіграє це рішення приблизно за 10 цілих секунд - тоді як такий підхід займає близько 12 секунд, list(set(..))займає лише близько 2 секунд!
dylnmc

@dylnmc - це також дублікат значно старшої відповіді
Елі Корвіго

4

Ось найшвидше пітонічне рішення порівняно з іншими, переліченими у відповідях.

Використання деталей впровадження оцінки короткого замикання дозволяє використовувати розуміння списку, яке досить швидко. visited.add(item)завжди повертається Noneяк результат, який оцінюється як False, тому права частина orзавжди буде результатом такого вираження.

Час це самостійно

def deduplicate(sequence):
    visited = set()
    adder = visited.add  # get rid of qualification overhead
    out = [adder(item) or item for item in sequence if item not in visited]
    return out

4

Використовуючи набір :

a = [0,1,2,3,4,3,3,4]
a = list(set(a))
print a

Використання унікальних :

import numpy as np
a = [0,1,2,3,4,3,3,4]
a = np.unique(a).tolist()
print a

4

На жаль, Більшість відповідей тут або не зберігають порядок, або занадто довгі. Ось проста відповідь на збереження замовлення.

s = [1,2,3,4,5,2,5,6,7,1,3,9,3,5]
x=[]

[x.append(i) for i in s if i not in x]
print(x)

Це дасть вам x із видаленими дублікатами, але збереження порядку.


3

Дуже простий спосіб у Python 3:

>>> n = [1, 2, 3, 4, 1, 1]
>>> n
[1, 2, 3, 4, 1, 1]
>>> m = sorted(list(set(n)))
>>> m
[1, 2, 3, 4]

2
sorted(list(...))є надмірним ( sortedвже неявно перетворює свій аргумент на новий list, сортує його, потім повертає новий list, тому використовуючи обидва засоби, роблячи непотрібне тимчасове list). Використовуйте лише в тому listвипадку, якщо результат не потребує сортування, використовуйте лише sortedякщо результат потрібно сортувати.
ShadowRanger

3

Магія пітона вбудованого типу

У python дуже легко обробляти такі складні випадки, як цей, і лише вбудований тип python.

Дозвольте мені показати вам, як це робити!

Спосіб 1: Загальний випадок

Спосіб ( 1 код рядка ) для видалення дубліфікованого елемента в списку і при цьому зберігати порядок сортування

line = [1, 2, 3, 1, 2, 5, 6, 7, 8]
new_line = sorted(set(line), key=line.index) # remove duplicated element
print(new_line)

Ви отримаєте результат

[1, 2, 3, 5, 6, 7, 8]

Метод 2: Особливий випадок

TypeError: unhashable type: 'list'

Спеціальний випадок для обробки непридатних ( 3 коду рядка )

line=[['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']]

tuple_line = [tuple(pt) for pt in line] # convert list of list into list of tuple
tuple_new_line = sorted(set(tuple_line),key=tuple_line.index) # remove duplicated element
new_line = [list(t) for t in tuple_new_line] # convert list of tuple into list of list

print (new_line)

Ви отримаєте результат:

[
  ['16.4966155686595', '-27.59776154691', '52.3786295521147'], 
  ['17.6508629295574', '-27.143305738671', '47.534955022564'], 
  ['18.8051102904552', '-26.688849930432', '42.6912804930134'], 
  ['19.5504702331098', '-26.205884452727', '37.7709192714727'], 
  ['20.2929416861422', '-25.722717575124', '32.8500163147157']
]

Оскільки кортеж є хешируемым, і ви можете легко конвертувати дані між списком і кортежем

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.