Множества в Python — set для аналитика
Коротко
Set (множество) — неупорядоченная коллекция уникальных элементов. Главное свойство — автоматическое удаление дубликатов и быстрая проверка вхождения за O(1). Для аналитика set полезен при работе с уникальными значениями: уникальные пользователи, пересечение аудиторий, разность списков. На собеседованиях множества встречаются в задачах на уникальность, сравнение коллекций и оптимизацию.
Создание множества
# Фигурные скобки
colors = {'red', 'green', 'blue'}
# Из списка — удалит дубликаты
numbers = set([1, 2, 3, 2, 1])
print(numbers) # {1, 2, 3}
# Пустое множество — только через set()
empty = set() # правильно
not_a_set = {} # это dict, не set!
# Из строки — по символам
chars = set('hello')
print(chars) # {'h', 'e', 'l', 'o'}
# Генератор множества (set comprehension)
squares = {x**2 for x in range(10)}
print(squares) # {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}Важно: {} создаёт пустой dict, а не set. Для пустого множества — set().
Уникальные значения из списка
Самое частое применение в аналитике — быстрое получение уникальных значений:
user_ids = [101, 202, 101, 303, 202, 404, 101]
unique_users = set(user_ids)
print(unique_users) # {101, 202, 303, 404}
print(len(unique_users)) # 4
# Количество уникальных значений в одну строку
n_unique = len(set(user_ids))В pandas аналог — df['col'].nunique() или df['col'].unique(), но для чистого Python set — самый быстрый способ.
Добавление и удаление элементов
s = {1, 2, 3}
# Добавить элемент
s.add(4) # {1, 2, 3, 4}
s.add(2) # {1, 2, 3, 4} — дубликат игнорируется
# Удалить элемент
s.remove(3) # {1, 2, 4} — KeyError если нет
s.discard(99) # ничего не произойдёт, без ошибки
s.pop() # удалит произвольный элемент
# Очистить
s.clear() # set()remove() выбросит KeyError, если элемента нет. discard() — безопасная альтернатива, удалит если есть, ничего не сделает если нет.
Операции над множествами
Множества поддерживают математические операции — объединение, пересечение, разность, симметрическая разность.
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}
# Объединение — все элементы из обоих
a | b # {1, 2, 3, 4, 5, 6, 7, 8}
a.union(b) # то же самое
# Пересечение — общие элементы
a & b # {4, 5}
a.intersection(b) # то же самое
# Разность — в a, но не в b
a - b # {1, 2, 3}
a.difference(b) # то же самое
# Симметрическая разность — в a или в b, но не в обоих
a ^ b # {1, 2, 3, 6, 7, 8}
a.symmetric_difference(b)| Операция | Оператор | Метод | Результат |
|---|---|---|---|
| Объединение | a | b |
a.union(b) |
Всё из a и b |
| Пересечение | a & b |
a.intersection(b) |
Общие элементы |
| Разность | a - b |
a.difference(b) |
В a, но не в b |
| Симм. разность | a ^ b |
a.symmetric_difference(b) |
Уникальные для каждого |
Проверка вхождения и подмножества
s = {1, 2, 3, 4, 5}
# Проверка вхождения — O(1)
3 in s # True
99 in s # False
99 not in s # True
# Подмножество
{1, 2}.issubset(s) # True — {1,2} ⊂ s
{1, 2} <= s # True
# Надмножество
s.issuperset({1, 2}) # True — s ⊃ {1,2}
s >= {1, 2} # True
# Непересекающиеся
{1, 2}.isdisjoint({3, 4}) # True — нет общих
{1, 2}.isdisjoint({2, 3}) # False — есть общий: 2Проверка x in set работает за O(1) благодаря хеш-таблице. Для списка проверка x in list — O(n). Если нужно много проверок вхождения, конвертируйте список в set.
Аналитические примеры
Пересечение аудиторий
campaign_a_users = {101, 202, 303, 404, 505}
campaign_b_users = {303, 404, 606, 707}
# Кто видел обе кампании
overlap = campaign_a_users & campaign_b_users
print(overlap) # {303, 404}
# Кто видел только кампанию A
only_a = campaign_a_users - campaign_b_users
print(only_a) # {101, 202, 505}
# Общий охват
total_reach = campaign_a_users | campaign_b_users
print(len(total_reach)) # 7Новые и ушедшие пользователи
users_march = {1, 2, 3, 4, 5}
users_april = {3, 4, 5, 6, 7}
new_users = users_april - users_march # {6, 7}
churned_users = users_march - users_april # {1, 2}
retained_users = users_march & users_april # {3, 4, 5}
retention_rate = len(retained_users) / len(users_march)
print(f'Retention: {retention_rate:.0%}') # Retention: 60%Быстрая фильтрация по списку ID
all_orders = [
{'user_id': 1, 'amount': 500},
{'user_id': 2, 'amount': 300},
{'user_id': 3, 'amount': 800},
{'user_id': 4, 'amount': 150},
]
premium_ids = {1, 3} # set для быстрой проверки
premium_orders = [
o for o in all_orders if o['user_id'] in premium_ids
]Проверка in premium_ids — O(1). Если бы premium_ids был списком — O(n) на каждую проверку.
Frozenset — неизменяемое множество
fs = frozenset([1, 2, 3])
# fs.add(4) # AttributeError — нельзя изменить
# Frozenset можно использовать как ключ dict или элемент другого set
d = {frozenset({1, 2}): 'group_a'}Frozenset — аналог tuple для множеств. Хешируемый, можно использовать как ключ словаря или элемент другого множества.
Set vs list — когда что
| Критерий | set | list |
|---|---|---|
| Порядок | Не гарантирован | Сохраняется |
| Дубликаты | Нет | Да |
Проверка in |
O(1) | O(n) |
Индексация [i] |
Нельзя | Можно |
| Изменяемый | Да | Да |
Используйте set, когда: нужны уникальные значения, частые проверки вхождения, операции над множествами. Используйте list, когда: важен порядок, нужна индексация, допускаются дубликаты.
Типичные ошибки
Пустое множество через {}. {} — это dict, не set. Для пустого множества: set().
Мутабельные элементы. Set хранит только хешируемые (неизменяемые) элементы. Списки и словари нельзя добавить в set — будет TypeError. Используйте tuple вместо list, frozenset вместо set.
s = set()
# s.add([1, 2]) # TypeError: unhashable type: 'list'
s.add((1, 2)) # OK — tuple хешируемыйПорядок не гарантирован. {3, 1, 2} может вывестись как {1, 2, 3}, но рассчитывать на порядок нельзя. Если нужен отсортированный уникальный список: sorted(set(data)).
Вопросы с собеседований
-- Чем set отличается от list? -- Set — неупорядоченная коллекция уникальных элементов. Проверка вхождения O(1) вместо O(n). Нет индексации, нет дубликатов, порядок не гарантирован.
-- Как быстро убрать дубликаты из списка?
-- list(set(data)) — но порядок потеряется. Если нужен порядок: list(dict.fromkeys(data)) — в Python 3.7+ dict сохраняет порядок вставки.
-- Что можно хранить в set? -- Только хешируемые (immutable) объекты: числа, строки, tuple, frozenset, bool, None. Нельзя: list, dict, set.
-- Как найти общие элементы двух списков за O(n)?
-- Преобразовать оба в set и взять пересечение: set(a) & set(b). Без set — вложенный цикл O(n²).
-- Что такое frozenset? -- Неизменяемый аналог set. Можно использовать как ключ словаря или элемент другого множества, потому что frozenset хешируемый.
Потренируйтесь решать задачи — откройте тренажёр с 1500+ вопросами для подготовки к собеседованиям аналитиков.
FAQ
Когда set быстрее list?
При проверке вхождения (x in collection) — всегда. Set использует хеш-таблицу: проверка за O(1). List ищет перебором: O(n). Если проверяете вхождение в цикле — конвертируйте list в set заранее.
Можно ли сортировать set?
Напрямую нельзя — у set нет метода sort(). Но sorted(my_set) вернёт отсортированный список. Если нужна и уникальность, и порядок — используйте dict.fromkeys() или sorted(set(data)).
Set в pandas — зачем?
В pandas множества полезны для фильтрации: df[df['user_id'].isin(set_of_ids)]. Метод isin() внутри использует set для быстрой проверки. Для подсчёта уникальных значений — nunique() и unique().
Как тренироваться
Set — базовая структура данных Python, которая часто появляется на собеседованиях аналитиков. В тренажёре Карьерник есть вопросы по структурам данных Python. Больше вопросов — в разделе с примерами.