Обработка коллекций: поиск неповторяющихся значений в Python

Раздел: Коллекции -> Обработка коллекций

Получение уникальных значений из коллекции

Как получить набор уникальных элементов без сохранения исходного порядка?

Решение: использовать встроенный тип set. Множество автоматически хранит только уникальные элементы, не гарантируя порядок.

numbers = [1, 2, 2, 3, 4, 4, 5]
unique_numbers = list(set(numbers))
print(unique_numbers)

уникальные значения python (уникальные значения в python)

[1, 2, 3, 4, 5]  (порядок может отличаться)

Цель: быстро удалить дубликаты, когда порядок не важен (например, проверка вхождения, математические операции над множествами).

Типичные проблемы:

  • Потеря исходного порядка – если порядок критичен, это решение не подходит.
  • Элементы должны быть хешируемыми (неизменяемыми). Списки, словари и другие изменяемые объекты вызовут TypeError: unhashable type. Решение: преобразовать во что-то неизменяемое (например, кортеж) или использовать другой подход.
  • При работе с большими объёмами данных set потребляет дополнительную память – в таких случаях можно использовать потоковую обработку и хранить уникальные значения в отдельном множестве.

Как сохранить порядок появления уникальных элементов?

Использовать dict.fromkeys() (начиная с Python 3.7 словарь сохраняет порядок вставки) или OrderedDict в более старых версиях.

values = ['a', 'b', 'a', 'c', 'b', 'd']
unique_ordered = list(dict.fromkeys(values))
print(unique_ordered)
['a', 'b', 'c', 'd']

Цель: удалить дубликаты, сохранив первое вхождение каждого элемента.

Неэффективно при очень большом количестве элементов из-за внутреннего хранения словаря. Для старых версий Python (ниже 3.7) порядок не гарантируется, требуется OrderedDict.

Как получить уникальные элементы вместе с их количеством?

Применить collections.Counter.

from collections import Counter
data = [1, 1, 2, 3, 3, 3, 4]
counter = Counter(data)
unique_items = list(counter.keys())   # уникальные значения
counts = list(counter.values())       # их количество
print(unique_items, counts)
[1, 2, 3, 4] [2, 1, 3, 1]

Цель: не только избавиться от дубликатов, но и узнать частоту встречаемости.

Аналогичные ограничения по хешируемости. Для сортировки по частоте требуется дополнительный шаг.

Как извлечь уникальные значения из всех вложенных структур (список списков)?

Рекурсивно пройти по всем элементам, собирая уникальные.

def flatten_and_unique(nested_list):
    result = set()
    def recurse(item):
        if isinstance(item, list):
            for sub in item:
                recurse(sub)
        else:
            result.add(item)
    recurse(nested_list)
    return list(result)

nested = [[1, 2], [2, 3, [4, 5]], 5]
print(flatten_and_unique(nested))
[1, 2, 3, 4, 5] (порядок случаен)

Цель: обработать глубоко вложенные коллекции, где элементы могут быть разных типов.

Рекурсия может превысить глубину стека для очень глубоких списков. Также не обрабатываются словари, кортежи и другие итерируемые - нужно расширять условие.

Как получить уникальные значения с использованием библиотеки Pandas?

Для табличных данных или при работе с Series.

import pandas as pd
series = pd.Series([10, 20, 10, 30, 20, 40])
unique_vals = series.unique()
print(unique_vals)
[10 20 30 40]

Цель: интеграция с экосистемой Pandas, автоматическая обработка NaN, сохранение порядка.

Избыточно для простых списков - требуется установка pandas. Работает только с одномерными данными.

Как объединить несколько списков и оставить только уникальные элементы?

Операция объединения множеств.

list1 = [1, 2, 3]
list2 = [2, 3, 4]
list3 = [4, 5, 6]
unique_union = list(set(list1) | set(list2) | set(list3))
print(unique_union)
[1, 2, 3, 4, 5, 6] (порядок не сохраняется)

Цель: слияние нескольких источников данных.

Если нужно сохранить порядок из первого списка, придётся использовать dict.fromkeys в цикле.

Как удалить дубликаты из списка с условием (например, оставить только последнее вхождение)?

Пройти список в обратном порядке и запоминать уже встреченные элементы.

items = ['x', 'y', 'x', 'z', 'y']
seen = set()
result = []
for item in reversed(items):
    if item not in seen:
        seen.add(item)
        result.append(item)
result.reverse()
print(result)
['x', 'z', 'y']  (первый 'x' и 'y' удалены)

Цель: гибкое управление тем, какое вхождение сохранить.

Требуется дополнительная логика, неэффективно для очень больших списков из-за прохода по памяти.

Расширенные примеры получения уникальных значений

1. Использование itertools.groupby для уникальных значений по ключу

Группировка последовательности и взятие первого элемента каждой группы. Подходит для отсортированных данных.

Пример
import itertools

pairs = [('a', 1), ('a', 2), ('b', 3), ('b', 4), ('c', 5)]
pairs.sort(key=lambda x: x[0])  # обязательно сортировка
unique_by_key = [next(group) for key, group in itertools.groupby(pairs, key=lambda x: x[0])]
print(unique_by_key)
[('a', 1), ('b', 3), ('c', 5)]

2. numpy.unique для числовых массивов

Работает с любыми numpy массивами, возвращает отсортированные уникальные значения.

Пример
import numpy as np
arr = np.array([3, 1, 2, 3, 4, 2, 1])
uniq = np.unique(arr)
print(uniq)
[1 2 3 4]

Также можно получить массив индексов для восстановления исходного массива, количества вхождений.

Пример
uniq, counts = np.unique(arr, return_counts=True)
print(uniq, counts)  # [1 2 3 4] [2 2 2 1]

3. Генератор для ленивого вычисления уникальных элементов

Позволяет обрабатывать большие потоки данных без хранения всего набора в памяти.

Пример
def unique_generator(iterable):
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item

large_list = [1, 2, 2, 3, 4, 3]
for u in unique_generator(large_list):
    print(u, end=' ')
1 2 3 4

4. Использование декоратора для мемоизации уникальных результатов функции

При вызове функции с одинаковыми аргументами возвращается уже вычисленное значение (кеширование).

Пример
from functools import lru_cache

@lru_cache(maxsize=None)
def expensive_calc(x):
    # Имитация тяжелых вычислений
    return x * x

values = [2, 3, 2, 4, 3, 5]
unique_results = {expensive_calc(v) for v in values}
print(unique_results)  # {4, 9, 16, 25}

5. Обработка уникальных строк из файла

Чтение файла построчно и сбор уникальных строк с сохранением порядка первого вхождения.

Пример
from collections import OrderedDict

unique_lines = []
with open('data.txt', 'r') as f:
    for line in f:
        stripped = line.strip()
        if stripped and stripped not in unique_lines:
            unique_lines.append(stripped)
print(unique_lines[:5])  # первые 5 уникальных строк

Более эффективно при большом файле: использовать OrderedDict или dict.fromkeys.

Пример
from collections import OrderedDict

with open('data.txt', 'r') as f:
    lines = (line.strip() for line in f if line.strip())
    unique_ordered = list(OrderedDict.fromkeys(lines))
print(unique_ordered[:5])

6. Использование filter с lambda для уникальности по определенному свойству

Например, оставить только уникальные значения длины строки.

Пример
words = ['cat', 'dog', 'elephant', 'cat', 'tiger', 'dog']
seen_lengths = set()
def is_new_length(word):
    length = len(word)
    if length not in seen_lengths:
        seen_lengths.add(length)
        return True
    return False

unique_by_len = list(filter(is_new_length, words))
print(unique_by_len)  # ['cat', 'elephant'] ('dog' и 'tiger' не попали, т.к. длина 3 и 5 уже есть)

7. Использование reduce для накопления уникальных значений (учебный пример)

Не рекомендуется из-за читаемости, но возможно.

Пример
from functools import reduce

numbers = [1, 2, 2, 3, 3, 4]
unique_reduce = reduce(lambda acc, x: acc if x in acc else acc + [x], numbers, [])
print(unique_reduce)  # [1, 2, 3, 4]

8. Собственный хеш для объектов пользовательских классов

Если нужно определить уникальность по собственным правилам, переопределяются методы __eq__ и __hash__.

Пример
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __eq__(self, other):
        return self.name == other.name
    def __hash__(self):
        return hash(self.name)
    def __repr__(self):
        return f"{self.name} ({self.age})"

people = [Person('Anna', 25), Person('Bob', 30), Person('Anna', 35)]
unique_people = list(set(people))
print(unique_people)  # [Anna (25), Bob (30)] - Anna (35) отброшена, т.к. name совпадает

9. Получение уникальных значений из нескольких кортежей с сохранением порядка объединения

Пример
t1 = (1, 2, 3)
t2 = (2, 3, 4)
t3 = (4, 5, 6)
merged = t1 + t2 + t3
unique = list(dict.fromkeys(merged))
print(unique)  # [1, 2, 3, 4, 5, 6]

Уникальные значения в Python - comments

En
уникальные значения python (python)