Pandas и DataFrame: вопросы для собеседования (часть 4)

DataFrame — основная структура данных в pandas и главный инструмент аналитика для работы с табличными данными в Python. На собеседовании проверяют фильтрацию, группировку, merge, работу с пропусками и типами данных. Часто дают датасет и просят посчитать метрику прямо в коде.

Коллекции и структуры данныхГенераторы списков и встроенные функцииЦиклы и условияИсключения и отладкаРабота с файлами: JSON и CSVФункции и аргументыИтераторы и генераторыNumPy: основыСинтаксис и типы данных

Вопросы 1620 из 20

16Что вернёт выражение `df.groupby("city").agg(total=("revenue", "sum"), avg=("revenue", "mean"))`?
AОбъект `Series` с двумя элементами `total` и `avg` по всей таблице без группировки
BТаблица `DataFrame` со строками исходных данных и новыми колонками `total` и `avg`
CТаблица `DataFrame` с индексом по `city` и колонками `total` и `avg` по выручке
DОбъект `Series` с мультииндексом из пар `city` и `total` со значениями сумм выручки
Ответ: `.agg()` с несколькими именованными агрегатами формирует итоговый `DataFrame` по группам с одной строкой на ключ.

После `.groupby("city")` данные разделяются по ключу `city`. Метод `.agg(...)` вычисляет агрегаты по каждой группе и возвращает итоговый `DataFrame`: по одной строке на город. В этом примере будут два столбца: сумма `revenue` (`total`) и среднее `revenue` (`avg`). По умолчанию ключ группировки `city` становится индексом результата.

Подробный разбор →
17Зачем добавляют `validate="one_to_one"` в `pd.merge(a, b, on="id", how="left", validate="one_to_one")`?
AЧтобы автоматически привести типы столбцов ключа `id` к одинаковому виду перед слиянием таблиц
BЧтобы удалить из результата строки с `NaN` после соединения и вернуть только полные совпадения по ключу
CЧтобы гарантировать сортировку результата соединения по ключу `id` в порядке возрастания значений
DЧтобы проверить, что ключ `id` уникален в обеих таблицах, и получить ошибку, если это не так
Ответ: Параметр `validate` помогает ловить ошибки кардинальности соединения (`one_to_one`, `one_to_many`).

`validate="one_to_one"` сообщает `pandas`, что вы ожидаете уникальный ключ `id` и в `a`, и в `b`. Если на практике обнаружатся дубликаты ключа хотя бы с одной стороны, `.merge()` выбросит исключение `MergeError`. Это полезно при сборке аналитических витрин, чтобы заранее защититься от неожиданного размножения строк. Параметр не приводит типы, не убирает `NaN` и не сортирует результат — за это отвечают другие шаги.

Подробный разбор →
18Как корректно проставить значение `1` в столбец `flag` для строк, где `x > 0`, чтобы не получить предупреждение `SettingWithCopyWarning` и не зависеть от копирования среза?
A`df[df['x'] > 0]['flag'] = 1` через двойную индексацию со срезом по условию
B`df.loc[df['x'] > 0, 'flag'] = 1` через выбор строк и столбца за один шаг по `.loc`
C`for i in range(len(df)): df.loc[i, 'flag'] = int(df.loc[i, 'x'] > 0)` циклом по строкам
D`df['flag'] = 1; df = df[df['x'] > 0]` через пометку всех строк и фильтрацию копии
Ответ: Для безопасного присваивания по условию используйте `.loc[условие, столбец] = ...`.

Выражение вида `df[условие]["col"] = ...` создаёт промежуточный объект и может изменить копию, а не исходный `DataFrame`, что приводит к предупреждению `SettingWithCopyWarning` и потенциальным ошибкам. Идиоматичный и надёжный способ — `df.loc[условие, "col"] = значение`: за один шаг выбираются нужные строки и столбец, и присваивание выполняется на исходном объекте. Цикл по строкам сработает, но это очень медленно на больших данных. Вариант с предварительной пометкой всех строк и последующей фильтрацией ломает исходную задачу: он меняет содержимое всего набора, а не только подмножества.

Подробный разбор →
19Нужно посчитать долю каждой строки в сумме продаж своей группы по `store`: `share = sales / sum(sales)` внутри `store`. Какой вариант самый идиоматичный в `pandas`?
A`df["share"] = df.groupby("store")["sales"].sum() / df["sales"].sum()`
B`df["share"] = df["sales"] / df.groupby("store")["sales"].mean()`
C`df["share"] = df["sales"] / df.groupby("store")["sales"].transform("sum")`
D`df["share"] = df["sales"].apply(lambda x: x / df.groupby("store")["sales"].sum())`
Ответ: Для расчёта значений на уровне строк внутри групп используют `.transform()`, который сохраняет исходную длину `DataFrame`.

`.transform("sum")` возвращает `Series` той же длины, что и исходный `DataFrame`, где каждой строке сопоставлена сумма её группы. Это позволяет делить `df["sales"]` поэлементно. Варианты с `.sum()` или `.mean()` без `transform()` дают агрегированный результат по группам и не совпадают по длине с исходными строками. `apply()` с обращением к `groupby` внутри лямбды работает медленно и непрозрачно, а ещё и считает не то — он применяет операцию к каждому скаляру.

Подробный разбор →
20Есть `events` со столбцами `user_id`, `event`, `ts` и таблица `users` со столбцом `user_id`. Нужно посчитать число покупок (`event == 'purchase'`) за 2025 год по пользователю и присоединить к `users`, сохранив всех пользователей. Какой вариант корректен?
AФильтр `event == 'purchase'`, агрегация `groupby('user_id').size()`, `reset_index` и `merge(..., how='inner')` без фильтра по году
BФильтр по типу события и `dt.year == 2025`, `groupby('user_id').size()`, `reset_index(name='purchase_cnt')`, `merge(..., how='left')`
CФильтр по типу события и году, `groupby('user_id').count()` без `reset_index`, далее `merge(..., how='left')` к таблице `users`
DБез фильтра по типу события и без года: `groupby('user_id').size()`, `reset_index(name='purchase_cnt')`, `merge(..., how='left')`
Ответ: Типичный пайплайн: фильтрация → `.groupby().size()` → `reset_index` → `.merge(..., how='left')`.

Сначала события фильтруются по типу и периоду через булеву индексацию. Затем агрегируют по `user_id` — удобный счётчик строк в группе даёт `.size()`. Чтобы результат стал плоским и легко мерджился, делают `reset_index(name='purchase_cnt')`. Наконец, `.merge(..., how='left')` сохраняет всех пользователей из `users`, добавляя счётчик покупок (у пользователей без покупок будет `NaN`, его при необходимости заменяют на 0). Варианты с `inner`, без фильтра или без `reset_index` ломают одно из этих требований.

Подробный разбор →
1234

Хотите тренировать интерактивно?

В приложении — таймер, прогресс, стрики и 1700+ вопросов по всем темам.

Тренировать Python в Telegram

Другие темы: Python

Коллекции и структуры данныхГенераторы списков и встроенные функцииЦиклы и условияИсключения и отладкаРабота с файлами: JSON и CSVФункции и аргументыИтераторы и генераторыNumPy: основыСинтаксис и типы данных