Есть ли встроенная функция, которая удаляет дубликаты из списка в Python, сохраняя при этом порядок? Я знаю, что могу использовать набор для удаления дубликатов, но это разрушает первоначальный порядок. Я также знаю, что я могу катиться так:
def uniq(input):
output = []
for x in input:
if x not in output:
output.append(x)
return output
(Спасибо, что раскрутите этот пример кода .)
Но я хотел бы воспользоваться встроенной или более Pythonic идиом, если это возможно.
Смежный вопрос: Каков самый быстрый алгоритм удаления дубликатов из списка в Python, чтобы все элементы были уникальными при сохранении порядка ?
источник
seen.add
мог меняться между итерациями, и среда выполнения недостаточно умна, чтобы исключить это. Чтобы играть безопасно, он должен проверять объект каждый раз. - Если вы посмотрите на байт-код сdis.dis(f)
, вы увидите, что он выполняетсяLOAD_ATTR
дляadd
члена на каждой итерации. ideone.com/tz1Tllseen_add
это улучшение, но системные ресурсы могут влиять на время. Было бы интересно увидеть полный таймингиseen_add = seen.add
дает только 1% увеличение скорости. Это вряд ли существенно.Редактировать 2016
Как отметил Рэймонд , в python 3.5+, где
OrderedDict
реализован на C, подход к пониманию списка будет медленнее, чемOrderedDict
(если вам не нужен список в конце - и даже тогда, только если ввод очень короткий). Так что лучшее решение для 3.5+ этоOrderedDict
.Важно Редактировать 2015
Как отмечает @abarnert ,
more_itertools
библиотека (pip install more_itertools
) содержитunique_everseen
функцию, которая создана для решения этой проблемы без каких-либо нечитаемых (not seen.add
) мутаций в списках. Это также самое быстрое решение:Всего одна простая библиотека импорта и никаких хаков. Это происходит от реализации рецепта itertools,
unique_everseen
который выглядит следующим образом:В Python
общепринятой общая идиома(который работает , но не оптимизирован для скорости, я бы сейчас использовать ) для этого применения :2.7+
unique_everseen
collections.OrderedDict
Время выполнения: O (N)
Это выглядит намного лучше, чем:
и не использует уродливый хак :
который опирается на тот факт, что
set.add
это метод на месте, который всегда возвращает,None
так чтоnot None
оцениваетTrue
.Однако обратите внимание, что решение для взлома быстрее в необработанной скорости, хотя оно имеет ту же сложность во время выполнения O (N).
источник
[seen.add(x) for x in seq if x not in seen]
, или, если вам не нравятся побочные эффекты понимания, просто используйтеfor
цикл:for x in seq: seen.add(x) if x not in seen else None
(все еще однострочное, хотя в этом случае я думаю, что однострочное - это глупое свойство пытаться иметь в решениеseen = set(seq)
.В Python 2.7 новый способ удаления дубликатов из итерируемого при сохранении его в исходном порядке:
В Python 3.5 OrderedDict имеет реализацию на языке C. Мои данные показывают, что сейчас это самый быстрый и самый короткий из различных подходов для Python 3.5.
В Python 3.6 обычный dict стал упорядоченным и компактным. (Эта функция поддерживается для CPython и PyPy, но может отсутствовать в других реализациях). Это дает нам новый самый быстрый способ дедупликации при сохранении порядка:
В Python 3.7 регулярный dict гарантированно упорядочен во всех реализациях. Итак, самое короткое и быстрое решение:
Реакция на @max: как только вы перейдете на 3.6 или 3.7 и будете использовать обычный dict вместо OrderedDict , вы не сможете по-настоящему побить производительность другим способом. Словарь плотный и легко преобразуется в список практически без накладных расходов. Целевой список предварительно масштабируется до len (d), который сохраняет все изменения, которые происходят при понимании списка. Кроме того, поскольку внутренний список ключей плотный, копирование указателей происходит почти так же быстро, как копирование списка.
источник
OrderedDict
в список в конце. Если мне нужно преобразовать его в список, для небольших входных данных подход к пониманию списка все еще быстрее, до 1,5 раз. Тем не менее, это решение намного чище.set()
поможет более наивным пользователям разрабатывать воспроизводимые коды.уникальный →
['1', '2', '3', '6', '4', '5']
источник
n^2
None
ссылок в процессе!)for
вместо этого петлюНе бить мертвую лошадь (этот вопрос очень старый и уже имеет много хороших ответов), но вот решение, использующее панд, которое довольно быстро во многих обстоятельствах и очень просто в использовании.
источник
Список даже не нужно сортировать , достаточным условием является то, что равные значения сгруппированы вместе.
Изменить: я предположил, что «сохранение порядка» подразумевает, что список на самом деле упорядочен. Если это не так, то решение от MizardX является правильным.
Редактирование сообщества: это, однако, самый элегантный способ «сжать повторяющиеся последовательные элементы в один элемент».
источник
Я думаю, если вы хотите поддерживать порядок,
Вы можете попробовать это:
ИЛИ аналогично вы можете сделать это:
Вы также можете сделать это:
Это также можно записать так:
источник
В Python 3.7 и выше словари гарантированно запоминают порядок вставки ключей. Ответ на этот вопрос обобщает текущее состояние дел.
Таким
OrderedDict
образом, решение становится устаревшим, и без каких-либо операторов импорта мы можем просто выдать:источник
Еще один очень поздний ответ на еще один очень старый вопрос:
У
itertools
рецептов есть функция, которая делает это, используяseen
заданную технику, но:key
функцию.seen.add
а не ищет его N раз. (f7
также делает это, но некоторые версии не делают.)ifilterfalse
, так что вам нужно только перебирать уникальные элементы в Python, а не все из них. (ifilterfalse
Конечно, вы все еще перебираете все из них внутри , но это в Си, и намного быстрее.)Это на самом деле быстрее, чем
f7
? Это зависит от ваших данных, поэтому вам придется проверить это и посмотреть. Если вам нужен список в конце,f7
используйте listcomp, и здесь нет способа сделать это. (Вы можете использовать напрямуюappend
вместоyield
ing, или вы можетеlist
подать генератор в функцию, но ни один из них не может быть таким же быстрым, как LIST_APPEND внутри listcomp.) Во всяком случае, обычно выжимание нескольких микросекунд не будет таким важно иметь легкую для понимания, повторно используемую, уже написанную функцию, которая не требует DSU, когда вы хотите украсить.Как и во всех рецептах, он также доступен в
more-iterools
.Если вам нужен только no-
key
case, вы можете упростить его как:источник
more-itertools
это явно лучший ответ. Простой,from more_itertools import unique_everseen
list(unique_everseen(items))
гораздо более быстрый подход, чем мой, и намного лучше, чем принятый ответ. Думаю, загрузка библиотеки того стоит. Я собираюсь в сообщество вики мой ответ и добавить это.Просто чтобы добавить еще один (очень производительную) реализации такой функциональности от внешнего модуля 1 :
iteration_utilities.unique_everseen
:Задержки
Я сделал несколько таймингов (Python 3.6), и они показывают, что это быстрее, чем все другие альтернативы, которые я тестировал, включая
OrderedDict.fromkeys
,f7
иmore_itertools.unique_everseen
:И просто чтобы убедиться, что я также провел тест с большим количеством дубликатов, просто чтобы проверить, имеет ли это значение:
И тот, который содержит только одно значение:
Во всех этих случаях
iteration_utilities.unique_everseen
функция самая быстрая (на моем компьютере).Эта
iteration_utilities.unique_everseen
функция также может обрабатывать непредсказуемые значения во входных данных (однако сO(n*n)
производительностью вместоO(n)
производительности, когда значения могут быть хэшируемыми).1 Отказ от ответственности: я автор этого пакета.
источник
seen_add = seen.add
- это нужно для оценки?dict.fromkeys()
метод в свой график, пожалуйста?ordereddict.fromkeys
?Для отсутствующих типов (например, списков), основанных на MizardX:
источник
Заимствование рекурсивной идеи, используемой при определении
nub
функции Хаскелла для списков, это будет рекурсивный подход:например:
Я попробовал это для растущих размеров данных и увидел сублинейную сложность времени (не является окончательной, но предполагает, что это должно быть хорошо для обычных данных).
Я также думаю, что интересно, что это может быть легко обобщено другими операциями. Нравится:
Например, вы можете передать функцию, которая использует понятие округления до того же целого числа, как если бы оно было «равенством» в целях уникальности, например так:
тогда unique (some_list, test_round) предоставил бы уникальные элементы списка, где уникальность больше не означала традиционное равенство (что подразумевается при использовании любого вида подхода на основе множеств или основанных на дикт-ключах к этой проблеме), а вместо этого означала принять только первый элемент, который округляется до K для каждого возможного целого числа K, к которому элементы могут округляться, например:
источник
filter
едва ли выиграет от предыдущего вызова вообще. Но если количество уникальных элементов мало по сравнению с размером массива, это должно работать довольно хорошо.В 5 раз быстрее уменьшить вариант, но более сложный
Объяснение:
источник
Вы можете ссылаться на понимание списка, поскольку оно строится символом '_ [1]'.
Например, следующая функция unique-ifies список элементов без изменения их порядка путем ссылки на его понимание списка.
Демо-версия:
Вывод:
источник
Ответ MizardX дает хорошую коллекцию нескольких подходов.
Вот что я придумал, думая вслух:
источник
O(n)
операция, и вы выполняете ее для каждого элемента, сложность вашего решения будет такойO(n^2)
. Это просто неприемлемо для такой тривиальной проблемы.Вот простой способ сделать это:
что дает вывод:
источник
Вы могли бы сделать что-то вроде уродливого взлома понимания списка.
источник
i,e in enumerate(l)
вl[i] for i in range(len(l))
.Относительно эффективный подход с
_sorted_
черезnumpy
массивы:Выходы:
источник
Выражение генератора, которое использует поиск O (1) набора, чтобы определить, следует ли включать элемент в новый список.
источник
extend
выражения с генератором, которое зависит от расширяемой вещи (т. Е. +1), ноset(n)
пересчитывается на каждой стадии (которая является линейной), что подрывает общий подход к квадратичности. На самом деле, это почти наверняка хуже, чем просто использованиеele in n
. Создание набора для одного теста членства не стоит затрат на создание набора. Все же - это интересный подход.Простое рекурсивное решение:
источник
Исключая дублирующиеся значения в последовательности, но сохраняйте порядок оставшихся элементов. Использование функции генератора общего назначения.
источник
Пользователи панд должны проверить
pandas.unique
.Функция возвращает массив NumPy. При необходимости вы можете преобразовать его в список с помощью
tolist
метода.источник
Если вам нужен один лайнер, то, возможно, это поможет:
... должно работать, но поправьте меня, если я ошибаюсь
источник
Если вы регулярно используете
pandas
, а эстетика предпочтительнее производительности, рассмотрите встроенную функциюpandas.Series.drop_duplicates
:Сроки:
источник
это сохранит порядок и будет выполняться за O (n) раз. в основном идея состоит в том, чтобы создать дыру там, где найден дубликат, и опустить его на дно. использует указатель чтения и записи. всякий раз, когда обнаруживается дубликат, только указатель чтения перемещается, а указатель записи остается в записи дубликата, чтобы перезаписать его.
источник
Решение без использования импортированных модулей или наборов:
Дает вывод:
источник
Метод на месте
Этот метод является квадратичным, потому что у нас есть линейный поиск в списке для каждого элемента списка (к этому мы должны добавить стоимость переупорядочения списка из-за
del
s).Тем не менее, можно работать на месте, если мы начнем с конца списка и перейдем к источнику, удаляя каждый термин, который присутствует в подсписке слева
Эта идея в коде просто
Простой тест реализации
источник
l[:] = <one of the the faster methods>
если вы хотите операцию на месте, нет?a=[1]; b=a; a[:]=[2]
тогдаb==[2]
значение равно,True
и мы можем сказать, что мы делаем это на месте, тем не менее, вы предлагаете использовать новое пространство, чтобы получить новый список, заменить старые данные новыми данными и отметить старые данные для сборки мусора, потому что на них больше ничего не ссылаются, так что если говорить о том, что они работают на месте, это немного расширяет концепцию относительно того, что, как я показал, возможно ... это неэффективно? да, но я сказал это заранее.Подход zmk использует понимание списка, которое очень быстро, но сохраняет порядок естественным образом. Для применения к чувствительным к регистру строкам его можно легко модифицировать. Это также сохраняет оригинальный случай.
Тесно связанные функции:
источник
Одно понимание списка лайнеров:
Просто добавьте условие, чтобы проверить, что значение не на предыдущей позиции
источник