Сначала зафиксируйте grain данных, допущения, метрику и риск leakage, затем обсуждайте модель, инструмент или инфраструктуру.
Вопросы и ответы
10 подробных ответов
01Чем Series отличается от DataFrame, как загрузить данные и быстро их осмотреть?
junior
Короткий ответ: Series — это одномерный массив с индексом (один столбец), DataFrame — двумерная таблица из колонок-Series с общим индексом строк. Данные грузят через pd.read_csv() / pd.read_sql(), а первичный осмотр делают через .head(), .info(), .describe(), .dtypes, .shape.
Подробно:
- Series vs DataFrame —
df['col']возвращаетSeries;df[['col']](двойные скобки) —DataFrameиз одной колонки. У обоих есть.index. - Загрузка —
read_csv(файл/URL),read_sql(SQL-запрос + соединение),read_parquet(быстрый колоночный формат). - Осмотр —
.head()/.tail()смотрят края,.info()даёт типы и пропуски,.describe()— статистику числовых колонок,.dtypes— типы,.shape— размер.
import pandas as pd
df = pd.read_csv("sales.csv", parse_dates=["order_date"])
df.shape # (10000, 8) — строки, колонки
df.info() # типы колонок и число непустых значений
df.describe() # count/mean/std/min/quartiles/max по числовым
df["revenue"].head() # Series — одна колонка
⚠️ Частая ошибка: забыть parse_dates= при чтении — колонка с датами останется object (строкой), и арифметика по датам/resample() работать не будут.
02В чём разница между loc и iloc, как фильтровать булевой маской и откуда берётся SettingWithCopyWarning?
middle
Короткий ответ: loc выбирает по меткам (имена строк/колонок), iloc — по целочисленным позициям. Фильтруют булевой маской: df[df['x'] > 0]. SettingWithCopyWarning возникает при цепочечном индексировании (df[mask]['col'] = ...), когда pandas не уверен, пишете ли вы в копию или во view.
Подробно:
loc[строки, колонки]— по меткам, границы включительны:df.loc[df['region']=='EU', ['revenue','qty']].iloc[строки, колонки]— по позициям, границы как у срезов Python (правая не включена):df.iloc[0:5, :2].- Булева маска — комбинируйте через
&/|и скобки вокруг каждого условия:(df['a']>0) & (df['b']<10).
# Правильно: одна операция loc на полном DataFrame
mask = (df["price"] > 100) & (df["in_stock"])
df.loc[mask, "tier"] = "premium" # пишем по месту, без копии
# Неправильно: цепочка -> SettingWithCopyWarning
# df[mask]["tier"] = "premium" # пишет во временную копию, df не меняется
⚠️ Частая ошибка: цепочечное присваивание df[mask]["col"] = val — изменение уходит во временную копию и теряется. Делайте одно df.loc[mask, "col"] = val; если работаете с подвыборкой дальше — берите явную .copy().
03Как работает groupby с агрегацией и почему это аналог GROUP BY из SQL?
middle
Короткий ответ: groupby реализует паттерн split-apply-combine: данные разбиваются по ключам, к каждой группе применяется агрегирующая функция, результаты собираются в один объект. Это прямой аналог GROUP BY. Несколько агрегаций удобно задавать через именованную агрегацию в .agg().
Подробно:
- Split —
df.groupby('region')строит группы по уникальным значениям ключа (можно список ключей). - Apply — функция на группу:
.sum(),.mean(),.size()или несколько разом через.agg(). - Combine — результат с группами в индексе;
reset_index()вернёт плоскую таблицу.
| SQL | pandas |
|---|---|
GROUP BY region |
df.groupby('region') |
SUM(revenue) |
.agg(rev=('revenue','sum')) |
COUNT(*) |
.size() |
HAVING SUM(x)>100 |
.loc[lambda g: g['rev']>100] |
out = (df.groupby("region")
.agg(revenue=("revenue", "sum"),
orders=("order_id", "nunique"),
avg_check=("revenue", "mean"))
.reset_index())
⚠️ Частая ошибка: по умолчанию groupby отбрасывает строки с NaN в ключе — пропуски в группирующей колонке молча выпадут из итога (используйте dropna=False, если они важны).
04Чем pd.merge отличается от concat, что задаёт how= и зачем нужен validate=?
middle
Короткий ответ: pd.merge соединяет таблицы по ключам (как SQL JOIN), pd.concat просто склеивает их по оси (стопкой строк или вбок по колонкам). Параметр how= задаёт тип join, а validate= ловит неожиданное размножение строк при join «многие-ко-многим».
Подробно:
| how= | что оставляет |
|---|---|
inner |
только совпавшие ключи (по умолчанию) |
left |
все строки левой + совпавшие правой |
outer |
объединение ключей обеих таблиц |
right |
все строки правой + совпавшие левой |
m = pd.merge(
orders, customers,
on="customer_id",
how="left",
validate="many_to_one", # упадёт, если справа ключ не уникален
indicator=True # колонка _merge: both/left_only/right_only
)
# concat — другая операция: складываем месяцы стопкой
year = pd.concat([jan, feb, mar], ignore_index=True)
⚠️ Частая ошибка: если ключ в правой таблице не уникален, merge молча размножает строки левой (декартово раздувание), и суммы оказываются завышены. Защита — validate="many_to_one" и проверка .shape до/после.
05Как чистить данные в pandas: пропуски, типы, даты и дубликаты?
middle
Короткий ответ: Пропуски находят через isna(), обрабатывают fillna() или dropna(); типы приводят astype() / to_numeric(); даты парсят to_datetime(); дубликаты убирают drop_duplicates(). Выбор «заполнить или удалить» зависит от доли пропусков и их природы.
Подробно:
- Найти пропуски —
df.isna().sum()показывает числоNaNпо колонкам. - Заполнить или удалить —
fillna(value)/fillna(df['x'].median())для замены;dropna(subset=[...])если строки без ключа бесполезны. - Типы и даты —
to_numeric(..., errors='coerce')превращает мусор вNaN;to_datetime()парсит даты. - Дубликаты —
duplicated()помечает,drop_duplicates(subset=[...], keep='last')удаляет.
df["price"] = pd.to_numeric(df["price"], errors="coerce")
df["signup"] = pd.to_datetime(df["signup"], errors="coerce")
df["price"] = df["price"].fillna(df["price"].median())
df = (df.dropna(subset=["customer_id"])
.drop_duplicates(subset=["order_id"], keep="last"))
⚠️ Частая ошибка: заполнять пропуски средним вслепую — это сдвигает распределение и занижает дисперсию. Сначала поймите, почему данные отсутствуют (MCAR/MAR/MNAR), и для скошенных величин берите медиану, а не mean.
06Чем pivot_table отличается от melt и когда аналитику нужен каждый из них?
middle
Короткий ответ: pivot_table разворачивает «длинный» формат в «широкий» (категории становятся колонками с агрегацией), melt делает обратное — собирает много колонок в пары «переменная–значение». Широкий формат удобен для отчёта-таблицы, длинный — для агрегаций и построения графиков.
Подробно:
| Операция | Направление | Когда нужна |
|---|---|---|
| pivot_table | long → wide | сводка-отчёт, кросс-таблица, матрица регион×месяц |
| melt | wide → long | привести «колонки-месяцы» к одному столбцу для groupby/plot |
# long -> wide: выручка по регионам и месяцам, с агрегацией
wide = df.pivot_table(index="region", columns="month",
values="revenue", aggfunc="sum", fill_value=0)
# wide -> long: разложить колонки-месяцы обратно в строки
long = wide.reset_index().melt(
id_vars="region", var_name="month", value_name="revenue")
⚠️ Частая ошибка: путать pivot и pivot_table. pivot падает на дубликатах пар индекс/колонка, а pivot_table их агрегирует (aggfunc). Для аналитики с возможными повторами почти всегда нужен pivot_table.
07Почему векторизация в pandas/NumPy быстрее циклов и apply, и когда apply неизбежен?
senior
Короткий ответ: Векторные операции выполняются в скомпилированном C над целыми массивами, без накладных расходов интерпретатора Python на каждую строку, поэтому они в десятки-сотни раз быстрее циклов и apply. apply оправдан только когда логику нельзя выразить векторно (сложная построчная функция, обращение к внешнему API).
Подробно:
- Векторизация — арифметика, сравнения,
np.where, строковые.str,.dtработают над всем столбцом сразу. apply/циклы медленны — каждая итерация дёргает Python-функцию; на миллионах строк это убивает производительность.- Когда apply нужен — нетривиальная построчная логика без векторного аналога; для условий предпочитайте
np.where/np.select. - Память —
categoryдля повторяющихся строк,downcastчисловых типов, чтение по частямchunksize=.
import numpy as np
# Векторно (быстро):
df["margin"] = (df["price"] - df["cost"]) / df["price"]
df["tier"] = np.where(df["price"] > 100, "premium", "basic")
# Вместо медленного:
# df["tier"] = df.apply(lambda r: "premium" if r.price > 100 else "basic", axis=1)
df["region"] = df["region"].astype("category") # экономия памяти
⚠️ Частая ошибка: df.apply(..., axis=1) как рефлекс — это скрытый построчный цикл Python. Сначала ищите векторный путь (np.where, np.select, .str, .dt); apply — крайняя мера.
08Как работать с временными рядами в pandas: datetime-индекс, resample и rolling?
middle
Короткий ответ: Для временных рядов нужен DatetimeIndex. resample() меняет частоту (даунсэмплинг: дни → недели/месяцы с агрегацией) — это «groupby по времени». rolling() строит скользящее окно фиксированного размера для скользящих средних и сглаживания.
Подробно:
- Datetime-индекс —
df = df.set_index('order_date'), индекс должен бытьdatetime(иначеresampleупадёт). - resample —
'D'день,'W'неделя,'ME'конец месяца,'QE'квартал; затем агрегатор:.sum(),.mean(). - rolling — окно по числу точек (
window=7) с.mean()/.sum();min_periodsуправляет краями.
ts = df.set_index("order_date").sort_index()
# Даунсэмплинг до месяца: сумма выручки
monthly = ts["revenue"].resample("ME").sum()
# Скользящее среднее за 7 дней по дневному ряду
daily = ts["revenue"].resample("D").sum()
ma7 = daily.rolling(window=7, min_periods=1).mean()
⚠️ Частая ошибка: звать resample() на колонке-строке или без datetime-индекса — будет TypeError. Сначала to_datetime() + set_index(); и не путайте resample (по календарному времени) с rolling (по числу строк).
09Как устроено построение графиков: figure/axes, df.plot() и где здесь seaborn?
junior
Короткий ответ: В matplotlib figure — это холст, а axes — конкретная система координат (один график) на нём. Быстро строить можно прямо из pandas через df.plot(kind=...), который рисует на axes. seaborn — надстройка над matplotlib для статистических графиков в пару строк и с красивыми дефолтами.
Подробно:
- figure vs axes —
fig, ax = plt.subplots()создаёт холст и оси; всё рисуется наax, подписи черезax.set_*. - df.plot() —
kind='line'|'bar'|'hist'|'scatter'; принимаетax=для размещения на нужных осях. - seaborn —
sns.histplot,sns.boxplot,sns.heatmap— статистика и группировки «из коробки», поверх тех же axes.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 4))
monthly.plot(kind="line", ax=ax, marker="o")
ax.set_title("Выручка по месяцам")
ax.set_ylabel("Выручка, ₽")
df["price"].plot(kind="hist", bins=30, ax=ax) # быстрая гистограмма
plt.tight_layout()
⚠️ Частая ошибка: в скрипте/ноутбуке плодить графики без plt.subplots() — всё валится на одни «текущие» оси (plt.gca()), и серии накладываются. Заводите явные fig, ax под каждый график.
10Когда считать в базе через SQL, а когда в pandas, и как операции SQL ложатся на pandas?
senior
Короткий ответ: Тяжёлую фильтрацию, агрегацию и джойны по большим таблицам делайте в базе (там индексы, оптимизатор и не нужно тянуть всё в память), а в pandas грузите уже урезанный результат для гибкой доводки, итеративного анализа и визуализации. Большинство операций SQL имеют прямой аналог в pandas.
Подробно:
- В базе — когда данные не влезают в RAM, когда фильтр/агрегация резко сокращают объём, когда есть индексы.
- В pandas — когда данные уже невелики, нужна итеративность, сложные преобразования, графики, ML-фичи.
| SQL | pandas |
|---|---|
WHERE x > 0 |
df[df.x > 0] |
SELECT a, b |
df[['a','b']] |
GROUP BY g |
df.groupby('g').agg(...) |
JOIN ON k |
pd.merge(a, b, on='k') |
ORDER BY x DESC |
df.sort_values('x', ascending=False) |
LIMIT 10 |
df.head(10) |
# Тяжёлую работу — БД, тонкую доводку — pandas
q = "SELECT region, revenue, order_date FROM sales WHERE revenue > 0"
df = pd.read_sql(q, conn) # уже отфильтровано на стороне БД
monthly = (df.groupby("region").agg(rev=("revenue", "sum")))
⚠️ Частая ошибка: SELECT * всей таблицы и фильтрация уже в pandas — вы зря гоните гигабайты по сети и упираетесь в память. Отдавайте WHERE/GROUP BY базе, в pandas тяните минимально нужный срез.
Источники
Источники и редакционная политика
Материалы RecallDeck сопоставлены с официальной документацией и открытыми публикациями компаний, когда первичный источник доступен. Мы не связаны с упомянутыми работодателями, не публикуем конфиденциальные задания и не продаём места в подборках. Формат найма может меняться — уточняйте его у рекрутера.
От чтения к воспроизведению
Отрепетируйте полный цикл интервью.
RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.