Удаление дубликатов в списках


1000

В значительной степени мне нужно написать программу, чтобы проверить, есть ли в списке дубликаты, и если он это делает, он удаляет их и возвращает новый список с элементами, которые не были дублированы / удалены. Это то, что у меня есть, но, если честно, я не знаю, что делать.

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))
Шаян Амани

Ответы:


1645

Обычный подход для получения уникальной коллекции предметов заключается в использовании 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 Вот что говорится в последнем абзаце.
совать

Ой ой. Должен был прочитать все это. В итоге я использовал кортежи вместо списков, чтобы этот подход мог работать.
Нежад

добавьте это к примеру, t = [3, 2, 1, 1, 2, 5, 6, 7, 8], ясно показывает разницу!
sailfish009

«... накладные расходы на создание словаря в первую очередь ... Если вам на самом деле не нужно сохранять порядок, лучше использовать набор». - Я сделал это, потому что мне было любопытно, правда ли это на самом деле. Мои тайминги показывают, что действительно набор немного быстрее: 1,12 мкс на цикл (набор) против 1,53 мкс на цикл (dict) в циклах 1M с абсолютной разницей во времени около 4 с в течение 1 м итераций. Так что, если вы делаете это в тесном внутреннем цикле, вам может быть все равно, иначе, вероятно, нет.
Миллердев

414

В Python 2.7 новый способ удаления дубликатов из итерируемого при сохранении его в исходном порядке:

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

В Python 3.5 OrderedDict имеет реализацию на языке C. Мои данные показывают, что сейчас это самый быстрый и самый короткий из различных подходов для Python 3.5.

В Python 3.6 обычный dict стал упорядоченным и компактным. (Эта функция поддерживается для CPython и PyPy, но может отсутствовать в других реализациях). Это дает нам новый самый быстрый способ дедупликации при сохранении порядка:

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

В Python 3.7 регулярный dict гарантированно упорядочен во всех реализациях. Итак, самое короткое и быстрое решение:

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

10
Я думаю, что это единственный способ держать вещи в порядке.
Герберт Амарал


5
@MartijnPieters Исправление: я думаю, что это единственный простой способ держать вещи в порядке.
Герберт Амарал

12
Для этого тоже содержимое исходного списка должно быть хэшируемым
Davide

Как упомянул @Davide, оригинальный список должен быть хэшируемым. Это означает, что это не работает для списка словарей. TypeError: unhashable type: 'dictlist'
CraZ

187

Это однострочник: list(set(source_list))сделает свое дело.

А setэто то, что не может иметь дубликаты.

Обновление: подход к сохранению порядка состоит из двух строк:

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

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


5
Это работает, только если source_listможно хэшировать.
Адриан

@AdrianKeister: это правда. Существуют объекты, которые имеют разумную семантику равенства, но не являются хэшируемыми, например списки. OTOH, если у нас не может быть ярлыка, подобного hastable, мы получим квадратичный алгоритм, который просто сравнивает каждый элемент со всеми известными в настоящее время уникальными элементами. Это может быть вполне нормально для коротких входов, особенно с большим количеством дубликатов.
9000

Точно, точно. Я думаю, что ваш ответ будет более качественным, если вы примете во внимание этот очень распространенный вариант использования.
Адриан

95
>>> 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: Not sure 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 Можете ли вы объяснить, какой более эффективный механизм кода существует в множестве и OrderedDict, который может сделать их менее трудоемкими? (исключая накладные расходы на их загрузку)
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

Результат не так плох, как с OrderedDict , но все же более чем в 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)]
DrD

25

Есть также решения, использующие Pandas и Numpy. Они оба возвращают массив 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 решение

Используя функцию 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, который является беспорядком и не будет работать для строк.
user227666

1
@ user227666 спасибо за ваш отзыв, но это не так, он работает даже со строкой, и вы можете добавить .tolist, если хотите получить список ...
GM

1
Я думаю, что это похоже на попытку убить пчелу кувалдой. Работает, конечно! Но импортирование библиотеки только для этой цели может быть немного излишним, нет?
Debosmit Ray

@DebosmitRay может быть полезно, если вы работаете в Data Science, где обычно вы работаете с NumPy и много раз вам нужно работать с NUMPY массивом.
GM

лучший ответ в 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. Однако это решение также ограничено хэшируемыми ключами.

Чтобы разрешить неиспользуемые ключи в Counter, я создал класс Container, который попытается получить хеш-функцию объекта по умолчанию, но в случае сбоя он попытается использовать функцию идентификации. Он также определяет eq и метод хэширования . Этого должно быть достаточно, чтобы в нашем решении не было нежелательных предметов. Нежелательные объекты будут обрабатываться так, как если бы они были хешеными. Тем не менее, эта хеш-функция использует идентичность для не подлежащих обработке объектов, а это означает, что два равных объекта, которые оба не подлежат обработке, не будут работать. Я предлагаю вам переопределить это и изменить его на использование хэша эквивалентного изменяемого типа (например, использование hash(tuple(my_list))if my_listis is list).

Я также сделал два решения. Другое решение, которое поддерживает порядок элементов, используя подкласс OrderedDict и Counter, который называется OrderedCounter. Теперь вот функции:

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 .

Я разделил это на три этапа построения графиков. Хэшируемый - это любой объект, который может быть хэширован, а неэхируемый - это любой объект, который не может быть хеширован. Упорядоченная последовательность - это последовательность, которая сохраняет порядок, неупорядоченная последовательность не сохраняет порядок. Теперь, вот еще несколько терминов:

Unordered Hashable был для любого метода, который удалял дубликаты, которые не обязательно должны поддерживать порядок. Это не должно было работать на непоправимые последствия, но могло.

Ordered Hashable предназначался для любого метода, который сохранял порядок элементов в списке, но он не должен был работать для непоправимых предметов, но мог.

Ordered Unhashable - это любой метод, который сохраняет порядок элементов в списке и работает на непоправимые убытки.

На оси Y указано количество секунд, которое потребовалось.

На оси абсцисс - номер, к которому была применена функция.

Мы сгенерировали последовательности для неупорядоченных хеш-хэлов и упорядоченных хеш-хеллов со следующим пониманием: [list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]

Для заказанных небрежных предметов: [[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]

Обратите внимание, что в диапазоне есть «шаг», потому что без него это заняло бы 10x больше времени. Кроме того, потому что, по моему личному мнению, я думал, что это могло бы выглядеть немного легче для чтения.

Также обратите внимание, что ключи на легенде - это то, что я пытался угадать как наиболее важные части функции. Что за функцию выполняет худший или лучший? График говорит сам за себя.

С этим улажено, вот графики.

Неупорядоченные хешаблы

введите описание изображения здесь (Увеличено) введите описание изображения здесь

Заказал Hashables

введите описание изображения здесь (Увеличено) введите описание изображения здесь

Заказано Небрежно

введите описание изображения здесь (Увеличено) введите описание изображения здесь


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с побочным эффектом еще более вводит в заблуждение, чем listcomp с побочным эффектом. Кроме того, lambda x: unique_list.append(x)это просто более медленный и медленный способ пройти unique_list.append.
Абарнерт

Очень полезный способ добавлять элементы в одну строку, спасибо!
ЗЛНК

2
@ZLNK, пожалуйста, никогда не используйте это. Помимо того, что концептуально уродливый, он также крайне неэффективен, потому что вы на самом деле создаете потенциально большой список и выбрасываете его только для выполнения базовой итерации.
Эли Корвиго

10

Все подходы к сохранению порядка, которые я видел здесь до сих пор, используют либо наивное сравнение (в лучшем случае с O (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)), зависящее только от длины списка.
loxaxs

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

@ 9000 Это правда. Я никогда не упоминал сложность времени подхода, основанного на хеш-таблицах, который, очевидно, равен O (n). Здесь вы можете найти много ответов, включая хеш-таблицы. Они не универсальны, потому что они требуют, чтобы объекты были хэшируемыми. Более того, они намного больше памяти.
Эли Корвиго

Требуется время, чтобы прочитать и понять этот ответ. Есть ли смысл перечислять, когда вы не используете индексы? 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, поэтому эффективно удаляет все дубликаты таким образом.


Также попробуйте это здесь
vineeshvs

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является линейной операцией, делающей ваше решение квадратичным.
Эли Корвиго

Вы правы. Но я также считаю, что вполне очевидно, что решение предназначено для того, чтобы сохранить один порядок. Все остальное уже здесь.
Atonal

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

@ MezZaveri рад.
Анураг Мисра

Создание новых списков и наборов не является бесплатным. Что произойдет, если мы сделаем это много раз в быстрой последовательности (т. Е. В очень узком цикле), а списки будут очень маленькими?
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)

['this', 'is', 'a', 'list', 'with', 'dupicates', 'in', '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 = sorted (list (set (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]

и порядок остается сохраненным.


Хотя это может сработать, использование тяжелой библиотеки, такой как pandas, для этой цели кажется излишним.
Glutexo

4

Этот заботится о заказе без особых хлопот (OrderdDict и другие). Вероятно, не самый Pythonic способ, ни кратчайший путь, но делает трюк:

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

К несчастью. Большинство ответов здесь либо не сохраняют порядок, либо являются слишком длинными. Вот простой ответ, сохраняющий порядок.

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)

Это даст вам х с удаленными дубликатами, но сохраняя порядок.


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.