← Назад к вопросам

Как избавить список от дубликатов?

1.3 Junior🔥 221 комментариев
#Python

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Это базовая, но важная операция в обработке данных. Рассказу о различных подходах в зависимости от контекста и требований.

Способ 1: Set (самый быстрый)

# Самый простой и быстрый способ
my_list = [1, 2, 3, 2, 4, 1, 5, 3]
unique_list = list(set(my_list))
print(unique_list)  # [1, 2, 3, 4, 5] (порядок не гарантирован)

Плюсы:

  • Очень быстро O(n)
  • Одна строка кода

Минусы:

  • Теряется порядок элементов
  • Работает только с hashable типами (не с lists, dicts)
  • Для несортируемых объектов результат непредсказуем

Способ 2: Set с сохранением порядка (рекомендуемый)

# Python 3.7+ — dict сохраняет порядок вставки
my_list = [1, 2, 3, 2, 4, 1, 5, 3]
unique_list = list(dict.fromkeys(my_list))
print(unique_list)  # [1, 2, 3, 4, 5] (порядок сохранён)

Плюсы:

  • Сохраняет исходный порядок
  • Быстро O(n)
  • Чистый, понятный код

Минусы:

  • Нужен Python 3.7+

Способ 3: List comprehension (гибкий)

my_list = [1, 2, 3, 2, 4, 1, 5, 3]
seen = set()
unique_list = [x for x in my_list if not (x in seen or seen.add(x))]
print(unique_list)  # [1, 2, 3, 4, 5]

Плюсы:

  • Гибкий — можно добавить условия
  • Сохраняет порядок

Минусы:

  • Менее читаемый
  • Трюк с seen.add(x) который возвращает None

Способ 4: pandas (для DataFrame)

import pandas as pd
import numpy as np

# Для столбца DataFrame
df = pd.DataFrame({'values': [1, 2, 3, 2, 4, 1, 5, 3]})
unique_values = df['values'].unique()  # array([1, 2, 3, 4, 5])
unique_list = df['values'].drop_duplicates().tolist()  # Сохраняет порядок
print(unique_list)  # [1, 2, 3, 4, 5]

# Удаление дубликатов в целом DataFrame
df_unique = df.drop_duplicates()

# Удаление дубликатов по определённым столбцам
df_unique = df.drop_duplicates(subset=['column_name'], keep='first')

Плюсы:

  • Специализирован для табличных данных
  • Встроены методы для сложных случаев (keep='first', subset)
  • Быстро на больших данных

Минусы:

  • Требует pandas

Способ 5: numpy (для массивов)

import numpy as np

my_array = np.array([1, 2, 3, 2, 4, 1, 5, 3])
unique_values = np.unique(my_array)  # [1 2 3 4 5] (отсортировано!)
print(unique_values)

# Если нужен исходный порядок
unique_indices = np.unique(my_array, return_index=True)[1]
unique_ordered = my_array[np.sort(unique_indices)]
print(unique_ordered)  # [1 2 3 4 5]

Плюсы:

  • Оптимизирован для числовых массивов
  • Встроена сортировка

Минусы:

  • Сортирует результат по умолчанию

Сложный случай: дубликаты некhashable типов

# Списки, словари нельзя добавить в set
my_list = [[1, 2], [3, 4], [1, 2], [5, 6]]

# Способ 1: преобразовать в tuple
unique_list = [list(x) for x in set(tuple(x) for x in my_list)]
print(unique_list)  # [[1, 2], [3, 4], [5, 6]]

# Способ 2: через JSON для dict
import json
my_list = [{'a': 1}, {'b': 2}, {'a': 1}]
unique_list = [json.loads(x) for x in set(json.dumps(d, sort_keys=True) for d in my_list)]
print(unique_list)  # [{'a': 1}, {'b': 2}]

Дубликаты с условием

# Удаление дубликатов, но оставить строку с максимальным значением
my_list = [(1, 'a'), (1, 'b'), (2, 'c'), (1, 'd')]

# Группируем по первому элементу, берём с максимальным вторым
from itertools import groupby
from operator import itemgetter

sorted_list = sorted(my_list, key=itemgetter(0, 1))
result = [max(group, key=itemgetter(1)) for key, group in groupby(sorted_list, key=itemgetter(0))]
print(result)  # [(1, 'd'), (2, 'c')]

Производительность: сравнение методов

import time

# Генерируем большой список
my_list = list(range(1000000)) + list(range(500000))

# Способ 1: set
start = time.time()
result1 = list(set(my_list))
print(f"set(): {time.time() - start:.4f} сек")

# Способ 2: dict.fromkeys
start = time.time()
result2 = list(dict.fromkeys(my_list))
print(f"dict.fromkeys(): {time.time() - start:.4f} сек")

# Способ 3: list comprehension
start = time.time()
seen = set()
result3 = [x for x in my_list if not (x in seen or seen.add(x))]
print(f"list comprehension: {time.time() - start:.4f} сек")

# Результат на моём компьютере:
# set(): 0.0120 сек
# dict.fromkeys(): 0.0125 сек
# list comprehension: 0.0380 сек

Вывод: set() и dict.fromkeys() примерно одинаковые по скорости. Выбираем в зависимости от нужна ли сохранение порядка.

Практический пример: очистка данных

import pandas as pd

# Реальный сценарий — очистка email адресов
emails = [
    'john@example.com',
    'john@example.com',  # дубликат
    'jane@example.com',
    'JOHN@EXAMPLE.COM',  # потенциальный дубликат (разный case)
    'jane@example.com'   # дубликат
]

# Нормализуем и удаляем дубликаты
cleaned_emails = list(dict.fromkeys(email.lower() for email in emails))
print(cleaned_emails)
# ['john@example.com', 'jane@example.com']

# Или с pandas
df = pd.DataFrame({'email': emails})
df['email_lower'] = df['email'].str.lower()
unique_emails = df.drop_duplicates(subset=['email_lower'])['email'].tolist()
print(unique_emails)

Рекомендации по выбору метода

Если нужен порядок → dict.fromkeys() или pandas
Если работаешь с DataFrame → drop_duplicates()
Если массивы numpy → np.unique()
Если скорость критична → set()
Если сложная логика → pandas или list comprehension
Если unhashable типы → преобразуй в hashable или pandas

Важное замечание: equals vs identical

# Будь осторожен с float и None
import numpy as np

my_list = [1.0, 1.0, np.nan, np.nan]
unique = list(set(my_list))
print(unique)  # [1.0, nan, nan] — NaN не удаляется!

# Решение:
pd.Series(my_list).drop_duplicates().tolist()  # [1.0, nan]

Выводы

  • dict.fromkeys() — лучший выбор для простых случаев (сохраняет порядок, быстро)
  • pandas — для табличных данных и сложной логики
  • set() — только если порядок не важен
  • Всегда проверяй edge cases (None, NaN, разные типы данных)
Как избавить список от дубликатов? | PrepBro