Перейти к содержанию
Данные и AI

10 вопросов по теме «аналитика данных: Python для аналитиков» на собеседовании

В этом материале — 10 вопросов из русской колоды RecallDeck по теме «аналитика данных: Python для аналитиков». Сначала сформулируйте короткий ответ сами, затем откройте подробный разбор и проверьте примеры, ограничения и отказные случаи.

9 мин чтения10 подробных ответовПроверено 24 августа 2026
Главная мысль

Сначала зафиксируйте grain данных, допущения, метрику и риск leakage, затем обсуждайте модель, инструмент или инфраструктуру.

Вопросы и ответы

10 подробных ответов

01

Чем Series отличается от DataFrame, как загрузить данные и быстро их осмотреть?

Короткий ответ: Series — это одномерный массив с индексом (один столбец), DataFrame — двумерная таблица из колонок-Series с общим индексом строк. Данные грузят через pd.read_csv() / pd.read_sql(), а первичный осмотр делают через .head(), .info(), .describe(), .dtypes, .shape.

Подробно:

  1. Series vs DataFramedf['col'] возвращает Series; df[['col']] (двойные скобки) — DataFrame из одной колонки. У обоих есть .index.
  2. Загрузкаread_csv (файл/URL), read_sql (SQL-запрос + соединение), read_parquet (быстрый колоночный формат).
  3. Осмотр.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?

Короткий ответ: 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?

Короткий ответ: groupby реализует паттерн split-apply-combine: данные разбиваются по ключам, к каждой группе применяется агрегирующая функция, результаты собираются в один объект. Это прямой аналог GROUP BY. Несколько агрегаций удобно задавать через именованную агрегацию в .agg().

Подробно:

  1. Splitdf.groupby('region') строит группы по уникальным значениям ключа (можно список ключей).
  2. Apply — функция на группу: .sum(), .mean(), .size() или несколько разом через .agg().
  3. 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=?

Короткий ответ: 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: пропуски, типы, даты и дубликаты?

Короткий ответ: Пропуски находят через isna(), обрабатывают fillna() или dropna(); типы приводят astype() / to_numeric(); даты парсят to_datetime(); дубликаты убирают drop_duplicates(). Выбор «заполнить или удалить» зависит от доли пропусков и их природы.

Подробно:

  1. Найти пропускиdf.isna().sum() показывает число NaN по колонкам.
  2. Заполнить или удалитьfillna(value) / fillna(df['x'].median()) для замены; dropna(subset=[...]) если строки без ключа бесполезны.
  3. Типы и датыto_numeric(..., errors='coerce') превращает мусор в NaN; to_datetime() парсит даты.
  4. Дубликаты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 и когда аналитику нужен каждый из них?

Короткий ответ: 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 неизбежен?

Короткий ответ: Векторные операции выполняются в скомпилированном C над целыми массивами, без накладных расходов интерпретатора Python на каждую строку, поэтому они в десятки-сотни раз быстрее циклов и apply. apply оправдан только когда логику нельзя выразить векторно (сложная построчная функция, обращение к внешнему API).

Подробно:

  1. Векторизация — арифметика, сравнения, np.where, строковые .str, .dt работают над всем столбцом сразу.
  2. apply/циклы медленны — каждая итерация дёргает Python-функцию; на миллионах строк это убивает производительность.
  3. Когда apply нужен — нетривиальная построчная логика без векторного аналога; для условий предпочитайте np.where / np.select.
  4. Память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?

Короткий ответ: Для временных рядов нужен DatetimeIndex. resample() меняет частоту (даунсэмплинг: дни → недели/месяцы с агрегацией) — это «groupby по времени». rolling() строит скользящее окно фиксированного размера для скользящих средних и сглаживания.

Подробно:

  1. Datetime-индексdf = df.set_index('order_date'), индекс должен быть datetime (иначе resample упадёт).
  2. resample'D' день, 'W' неделя, 'ME' конец месяца, 'QE' квартал; затем агрегатор: .sum(), .mean().
  3. 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?

Короткий ответ: В matplotlib figure — это холст, а axes — конкретная система координат (один график) на нём. Быстро строить можно прямо из pandas через df.plot(kind=...), который рисует на axes. seaborn — надстройка над matplotlib для статистических графиков в пару строк и с красивыми дефолтами.

Подробно:

  1. figure vs axesfig, ax = plt.subplots() создаёт холст и оси; всё рисуется на ax, подписи через ax.set_*.
  2. df.plot()kind='line'|'bar'|'hist'|'scatter'; принимает ax= для размещения на нужных осях.
  3. seabornsns.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?

Короткий ответ: Тяжёлую фильтрацию, агрегацию и джойны по большим таблицам делайте в базе (там индексы, оптимизатор и не нужно тянуть всё в память), а в 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, архитектуру и поведенческие истории.

Начать подготовку

Продолжить подготовку

Библиотека собеседований RecallDeck

Подробные русские ответы, разборы этапов найма и планы подготовки для российского IT-рынка.

RSS