Удаление дубликатов в списках - PullRequest
814 голосов
/ 01 ноября 2011

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

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

Ответы [ 45 ]

1369 голосов
/ 01 ноября 2011

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

374 голосов
/ 01 ноября 2011

В 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']
179 голосов
/ 01 ноября 2011

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

A set - это то, что не может иметь дубликатов.

Обновление: подход, сохраняющий порядокэто две строки:

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

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

82 голосов
/ 14 мая 2013
>>> 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]
76 голосов
/ 01 ноября 2011

Если вы не заботитесь о заказе, просто сделайте это:

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

A set гарантированно не будет иметь дубликатов.

37 голосов
/ 05 июля 2014

Создать новый список, сохраняющий порядок первых элементов дубликатов в 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]

Это проверяет, что каждый новый элемент ранее не появлялся в списке перед его добавлением. Также не нуждается в импорте.

22 голосов
/ 17 сентября 2014

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

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
20 голосов
/ 01 января 2014

Другой способ сделать:

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]
20 голосов
/ 03 июля 2014

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

16 голосов
/ 15 апреля 2015

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

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