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

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

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

30 ответов


общий подход для получения уникальной коллекции предметов-использовать set. Наборыненумерованный коллекции distinct объекты. Чтобы создать набор из любой итерации, вы можете просто передать его встроенному set()


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

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

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

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

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

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

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

это однострочный:list(set(source_list)) будет делать трюк.

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

Update: подход, сохраняющий порядок, состоит из двух строк:

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

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


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

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

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

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


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

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


другой вариант:

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

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

хорошо, что происходит, если set удаляется из решения?

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

есть также решения с использованием панд и 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']

решение не так элегантно по сравнению с другими, однако, по сравнению с пандами.unique (), numpy.unique () также позволяет проверить уникальность вложенных массивов вдоль одной выбранной оси.


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

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]

Я имел dict в моем списке, поэтому я не мог использовать вышеуказанный подход. Я получил ошибку:

TypeError: unhashable type:

Так что если вы заботитесь о ордер и / или некоторые элементы unhashable. Тогда вы можете найти это полезным:

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

попробуйте использовать наборы:

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

print t | t1
print t - t1

вы также можете сделать это:

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

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


все подходы, сохраняющие порядок, которые я видел здесь до сих пор, либо используют наивное сравнение (с O(N^2) временной сложностью в лучшем случае), либо тяжелый OrderedDicts/set+list комбинации, которые ограничиваются hashable входов. Вот хэш-независимое решение 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]]))] 

лучший подход к удалению дубликатов из списка-использование 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']

этот заботится о заказе без особых хлопот (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

уменьшить вариант с заказом сохранить:

предположим, что у нас есть список:

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 x быстрее, но более сложные

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

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

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]


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

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]

без использования set

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) 

вот самое быстрое решение pythonic, приходящее к другим, перечисленным в ответах.

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

используя set :

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

очень простой способ в 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]

В настоящее время вы можете использовать Counter class:

>>> import collections
>>> c = collections.Counter([1, 2, 3, 4, 5, 6, 1, 1, 1, 1])
>>> c.keys()
dict_keys([1, 2, 3, 4, 5, 6])

вот пример, возвращая список без repetiotions сохранение порядка. Не нуждается во внешнем импорте.

def GetListWithoutRepetitions(loInput):
    # return list, consisting of elements of list/tuple loInput, without repetitions.
    # Example: GetListWithoutRepetitions([None,None,1,1,2,2,3,3,3])
    # Returns: [None, 1, 2, 3]

    if loInput==[]:
        return []

    loOutput = []

    if loInput[0] is None:
        oGroupElement=1
    else: # loInput[0]<>None
        oGroupElement=None

    for oElement in loInput:
        if oElement<>oGroupElement:
            loOutput.append(oElement)
            oGroupElement = oElement
    return loOutput

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

>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> for i in t:
...     if i in t[t.index(i)+1:]:
...         t.remove(i)
... 
>>> t
[3, 1, 2, 5, 6, 7, 8]

Я думаю, что преобразование в set-самый простой способ удалить дубликат:

list1 = [1,2,1]
list1 = list(set(list1))
print list1

чтобы удалить дубликаты, сделайте его набором, а затем снова сделайте его списком и распечатайте/используйте его. Набор гарантированно имеет уникальные элементы. Например :

a = [1,2,3,4,5,9,11,15]
b = [4,5,6,7,8]
c=a+b
print c
print list(set(c)) #one line for getting unique elements of c

вывод будет следующим (проверено в python 2.7)

[1, 2, 3, 4, 5, 9, 11, 15, 4, 5, 6, 7, 8]  #simple list addition with duplicates
[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 15] #duplicates removed!!

вы можете сделать это просто с помощью наборов.

Шаг 1: получить различные элементы списков
Шаг 2 получить общие элементы списков
Шаг 3 объединить их

In [1]: a = ["apples", "bananas", "cucumbers"]

In [2]: b = ["pears", "apples", "watermelons"]

In [3]: set(a).symmetric_difference(b).union(set(a).intersection(b))
Out[3]: {'apples', 'bananas', 'cucumbers', 'pears', 'watermelons'}

def remove_duplicates(A):
   [A.pop(count) for count,elem in enumerate(A) if A.count(elem)!=1]
   return A

список comprehesion для удаления дубликатов