Обработка коллекций: поиск неповторяющихся значений в 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]