Лучший / самый питонный способ удалить дубликаты из списка и отсортировать их в обратном порядке

1

Я пытаюсь взять list (orig_list ниже) и вернуть list (new_list ниже), который:

  • не содержит повторяющихся элементов (т.е. содержит только уникальные элементы)
  • сортируется в обратном порядке

Вот что я до сих пор, что кажется... Я собираюсь сказать "странно", хотя я уверен, что есть лучший способ сказать это. Я в основном откладываю, используя list() дважды для того, что кажется довольно простым, и тогда мне интересно об эффективности этого подхода.

new_list = list(reversed(sorted(list(set(orig_list)))))

Вопрос № 1 (вопрос в стиле SO):

Правильны ли следующие предложения?

  1. Нет более эффективного способа получить уникальные элементы list чем преобразовать list в set и обратно.
  2. Поскольку на Python наборы не упорядочены, нужно сначала (1) преобразовать в набор, прежде чем удалять повторяющиеся элементы, потому что в противном случае вы потеряете сортировку в любом случае, и (2) вам нужно преобразовать обратно в список перед сортировкой.
  3. Использование list (reverseed()) программно эквивалентно использованию list.sort(reverseed = True).

Вопрос №2 (бонус):

Есть ли способы добиться того же результата в меньшем количестве Os, или использовать менее подробный подход? Если да, то какой из них является примером (-ами)?

  • 2
    После выполнения «сортировки по убыванию», которая имеет O (n lg n), можно удалить дубликаты в O (n). Это потому, что после сортировки любые дубликаты будут появляться рядом друг с другом.
Теги:
list
set
sorting

2 ответа

3
Лучший ответ
sorted(set(orig_list), reverse=True)

Самый короткий код, более эффективный, тот же результат.

В зависимости от размера, он может или не может быть быстрее отсортировать сначала, затем дедупую в линейном времени, как пользователь2864740 предлагает в комментариях. (Самый большой недостаток этого подхода - это полностью на Python, в то время как вышеприведенная строка выполняется в основном в собственном коде.)

Ваши вопросы:

  • Вам не нужно конвертировать из набора в список и обратно. sorted принимает любую итерацию, так квалифицирует набор, и выкладывает список, так что не требуется никакое последующее преобразование.

  • reversed(sorted(x)) не эквивалентно sorted(x, reverse=True). Вы получаете тот же результат, но медленнее - sort имеет одинаковую скорость, будь то прямая или обратная, поэтому reversed - это добавление дополнительной операции, которая не нужна, если вы отсортируете ее до правильного упорядочения с самого начала.

  • 1
    Очень хороший момент о том, где осуществляется реализация. Кроме того, если имеется «чрезмерное количество дубликатов», я бы предпочел сначала определить уникальный набор, как показано ... но это похоже на крайний случай.
2

Здесь у вас есть несколько расточительных шагов, но ваше предложение в основном правильное. Единственные реальные улучшения, которые нужно сделать, - избавиться от всех ненужных временных list s:

new_list = sorted(set(orig_list), reverse=True)

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

Единственное возможное улучшение в режиме большого времени - если вы знаете, что данные уже отсортированы, и в этом случае вы можете избежать сортировки O(n log n) и uniqify, не теряя существующий отсортированный порядок, используя itertools.groupby:

    new_list = [key for key, grp in itertools.groupby(orig_list)]

Если orig_list отсортирован в прямом порядке, вы можете сделать результат этого отмененного по существу без затрат, изменив itertools.groupby(orig_list) на itertools.groupby(reversed(orig_list)).

Решение groupby не очень практично для изначально несортированных входных данных, потому что, если дубликаты даже отдаленно распространены, удаление их посредством uniquification как шаг O(n) почти всегда стоит того, что уменьшает n в более дорогостоящем O(n log n) шаг сортировки. groupby также является относительно медленным инструментом; характер реализации, используя кучу временных итераторов для каждой группы, внутреннее кэширование значений и т.д., означает, что на практике это медленнее O(n) чем O(n) -эквивалентность через set, причем основным преимуществом является (делая его масштабируемым для наборов данных, передаваемых с диска или сети и обратно, без сохранения чего-либо в течение длительного времени, когда set должен вытащить все в память).

Другая причина использовать sorted + groupby была бы, если бы ваши данные не были хешируемыми, но были сопоставимы; в этом случае set не является вариантом, поэтому единственный выбор - сортировка и группировка.

  • 1
    Хороший показ того, как выполнить группировку отсортированных данных. «Как правило, итерируемое уже должно быть отсортировано по одной и той же ключевой функции» из документации itertools по groupby кажется groupby из-за использования «Обычно» .. (в C # или SQL, в качестве контрпримера, я бы ожидал «группа по» для работы с несортированными данными так же хорошо)
  • 2
    @ user2864740: Да, groupby аналогичен uniq из groupby GNU, это не комбинированный шаг сортировки + группировки, как в SQL. Тот факт, что он не сортируется, на самом деле полезен в некоторых случаях (для группировки прогонов данных в порядке их появления), поэтому они говорят «обычно», но, как и в случае с uniq , вам обычно нужно предшествовать с сортировкой если вам нужна только одна группа для каждого уникального ключа на протяжении всей итерации.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню