pandasPythonаналитикавременные рядысобеседование

10 функций pandas, которые заменяют цикл for: окна, сдвиги, временные ряды

2026-04-20 12 мин

Три года назад я писал 40 строк цикла, чтобы посчитать скользящее среднее по продажам. Сегодня — одна строка: df['revenue'].rolling(7).mean().

Если ты до сих пор итерируешь по DataFrame через for — ты пишешь код, который работает в 50 раз медленнее и содержит на 50 строк больше багов, чем аналитик уровнем выше.

Эта статья — разбор 10 функций pandas, которые появляются на каждом собеседовании в Яндекс, Ozon, Авито, Сбер и Тинькофф. Со сравнением "как было" и "как стало", с разбором частых ошибок и ссылками на задачи тренажёра.

Почему цикл for — это red flag на собеседовании
Интервьюер тратит 2 минуты на оценку "мыслит ли кандидат векторно". Цикл for row in df.iterrows() в pandas — сигнал, что человек не понимает, что такое Series и почему она быстрее. На мидла это уже минус.


Почему окна, сдвиги и ряды — это must-have

Аналитика — это сравнения. MoM, WoW, DoD, MA7, доли группы, ранги внутри когорты, прирост юзеров к прошлой неделе. Без этих функций ты не посчитаешь ни одну продуктовую метрику.

Задача из реальной работыБез pandasС pandas
Скользящее среднее 7 дней40 строк цикла.rolling(7).mean()
MoM выручки15 строк SQL self-join.pct_change() * 100
Прирост юзеров к прошлому днюloop + условия.diff()
Топ-3 магазина в городеsort + groupby + head + reset_index.rank(method='dense')
Недельная выручка из дневнойGROUP BY + DATE_TRUNC.resample('W').sum()
Доля магазина в выручке городаSQL window или subquery.transform('sum') + деление

Пойдём по функциям. Каждая — с реальным кейсом и кодом.


rolling() — скользящее окно

Классика: MA7 выручки, сглаживание шума, скользящее стандартное отклонение для детекции аномалий.

df['ma7'] = (
    df['revenue']
      .rolling(7, min_periods=1)
      .mean()
      .round(1)
)
Частая ошибка
Без min_periods=1 первые 6 значений будут NaN. Если считаешь дашборд — он покажет дырку в начале. Ставь 1, если хочешь видеть значения с первого дня.

На собесе спросят: "Посчитай MA7 выручки по каждому магазину". Ответ — df.groupby('shop')['revenue'].rolling(7).mean().


expanding() — накопительное окно

Когда окно растёт от начала до текущей строки. Накопительное среднее, накопительный максимум, расчёт running-total метрик.

df['avg_to_date'] = df['revenue'].expanding().mean().round(0)

Реальный кейс: "Средний чек с начала года до текущей даты" — одна строка. Без expanding() — цикл на 20 строк.


cumsum() — накопительная сумма

Суммирование нарастающим итогом. В связке с groupby — накопление по юзеру, по когорте, по дню.

df['total_spent'] = (
    df.groupby('user_id')['amount']
      .cumsum()
)

Где пригодится:


shift() — сдвиг колонки

Сдвигает значения на N строк. Основа для любых lag-фич: "выручка прошлого месяца", "действие за 1 шаг до покупки", "prev_value для A/B".

df['prev_revenue'] = (
    df.groupby('user_id')['revenue']
      .shift(1)
)
Сначала sort, потом shift
Если данные не отсортированы по дате — shift(1) вернёт случайную строку, а не "предыдущую во времени". Это один из самых дорогих багов в продакшене: метрика растёт, а по факту — мусор. Всегда: df.sort_values(['user_id', 'date']).groupby(...).shift().

На собесе: "Как посчитать повторные покупки за 30 дней?" — через shift даты предыдущей покупки + diff.


pct_change() — процентное изменение

MoM, WoW, DoD одной строкой. Самый частый вопрос на интервью по продуктовой аналитике.

df['mom_%'] = (
    df['revenue'].pct_change().mul(100).round(1)
)

До pandas это был бы SQL с self-join или LAG() window function. В pandas — один вызов.


diff() — абсолютная разница

Как pct_change, но в абсолютных единицах. Для счётчиков, приростов, дельт.

df['new_users'] = (
    df.sort_values('date')['users']
      .diff()
      .fillna(0)
)

Реальный кейс: из колонки cumulative DAU получить daily DAU. Одна строка вместо self-join.


rank() — ранжирование

Ранг внутри всей таблицы или внутри группы. method='dense' — без пропусков (1, 2, 2, 3), pct=True — перцентили.

df['shop_rank'] = (
    df.groupby('city')['revenue']
      .rank(ascending=False, method='dense')
)

Классика собеса: "Найди топ-3 магазина в каждом городе". Через rank + filter — 3 строки. Через sort + head + manual loop — страница кода.


nlargest() — топ-N без полного sort

Быстрее, чем sort_values().head(N), особенно на таблицах от миллиона строк.

top_products = df.nlargest(10, 'revenue')

Есть симметричный .nsmallest(). На собеседовании в Яндексе спрашивали: "чем nlargest отличается от sort+head" — ответ: O(N log k) против O(N log N).


resample() — ресемпл по времени

Из дневных данных — в недельные, месячные, квартальные. Требует DatetimeIndex.

df = df.set_index('date')
weekly = df['revenue'].resample('W').sum()

Алиасы: 'D' день, 'W' неделя, 'ME' конец месяца, 'QE' конец квартала. Агрегация как у groupby: sum, mean, ohlc, count.

Частая ошибка с 'M' vs 'ME'
В pandas 2.2+ алиас 'M' deprecated, правильно — 'ME' (month end) или 'MS' (month start). Код со старым 'M' выдаст warning, а потом и падение. Проверь версию: pd.__version__.


groupby().transform() — агрегация с broadcast

Агрегирует по группе, но возвращает массив исходного размера. Идеально для долей, z-score, нормализации внутри группы.

df['share_%'] = (
    df['revenue'] / df.groupby('city')['revenue'].transform('sum')
) * 100

Без transform это был бы groupby().sum() + merge обратно. Три строки вместо одной, плюс риск порчи индекса.

Что часто спрашивают: "Посчитай долю каждого заказа в дневной выручке". Ответ — transform('sum') по дате, деление.


Чеклист: готов ли ты к pandas-секции собеседования

Умею посчитать MoM за одну строку
Через pct_change или shift + деление. Без SQL self-join.

Знаю разницу между transform и agg
agg возвращает одну строку на группу, transform — массив исходного размера. Путать — минус балл на собесе.

Могу найти топ-N без полного sort
nlargest, rank method='dense', фильтр. Без перегруженного sort_values.

Понимаю когда rolling даёт NaN
min_periods, закрытие интервала, window на группу. Классическая ловушка в задаче на MA7.

Сортирую перед shift/diff
Без sort_values результат случайный. Это баг, который ловят на ревью, а не до него.

Знаю алиасы resample
D, W, ME, QE, YE. Понимаю разницу между 'ME' и 'MS'. Знаю что 'M' deprecated.

Если хотя бы один пункт вызывает сомнение — значит, собес по pandas ты сдаёшь не на максимум.

"Джун пишет цикл и считает, что работает с данными. Мидл пишет одну строку и считает, что думает о данных. Разница — в часах в день."


Что делать дальше

Прочитать статью — недостаточно. Pandas учится руками: открыл задачу, написал, увидел ошибку, исправил.

План на неделю:

Pro — это весь контент без лимитов
349 Python-задач, 300 SQL, 225 кейсов, 2000+ вопросов, 265 метрик с формулами, AI мок-собесы без ограничений. 1999₽/мес. Отбивается после первого успешного собеса на мидла — там +50-80К к ЗП.


Связанные материалы

Сохрани шпаргалку, открой тренажёр, реши первую задачу. Через неделю ты не узнаешь свой код.

Отработай pandas на 349 задачах
Интерактивный тренажёр с мгновенной проверкой. Rolling, groupby, merge — всё на реальных датасетах. 5 задач бесплатно.
Открыть тренажёр →