КПКейсПрактика
Все материалы
Продуктовая аналитикапрактикумсредний

Как тестировать аналитические расчёты на Python и pandas

Практический гайд по тестам для аналитика: проверить метрики на маленьком датасете, поймать регрессию и защитить расчёт от тихих изменений.

КПКейсПрактика9 августа 2026 г.12 мин
Два аналитика проверяют расчёт pandas по контрольному набору данных и отмечают результаты тестов.

Тесты нужны не только для backend-кода. Аналитический расчёт тоже может сломаться после переименования колонки, изменения фильтра или нового правила дедупликации. Самая опасная ошибка — не падение программы, а правдоподобная цифра, которая стала другой.

Коротко

Тест аналитического кода проверяет не стиль Python, а смысл результата: правильный знаменатель, уникальность пользователя, границы периода и ожидаемое поведение на маленьком наборе данных. Начать можно с нескольких DataFrame и обычных `assert`, а затем вынести проверки в pytest.

  • Пиши маленький тестовый датасет, где ответ можно посчитать вручную.
  • Проверяй не только тип и форму, но и конкретные значения метрики.
  • Отдельно тестируй крайние случаи: пустой период, дубликаты, пропуски и нулевой знаменатель.
  • Тесты должны быть независимыми от реального CSV и текущей даты.

Сначала вынеси расчёт в функцию

Код в ячейке сложно проверить, если он одновременно читает файл, фильтрует даты и рисует график. Вынеси бизнес-правило в функцию с DataFrame на входе. Тогда тест сможет передать маленький набор строк и сравнить результат с ожидаемым.

pythonРасчёт DAU по core action
import pandas as pd

def calculate_dau(events: pd.DataFrame) -> pd.DataFrame:
    active = events.loc[events['event_name'].eq('core_action')].copy()
    active['event_date'] = pd.to_datetime(active['occurred_at']).dt.date
    return (
        active.groupby('event_date', as_index=False)
        .agg(dau=('user_id', 'nunique'))
        .sort_values('event_date')
    )

Контрольный датасет должен быть маленьким и хитрым

Четыре строки, где каждый пользователь совершает действие один раз, проверяют только счастливый путь. Добавь повторное событие одного пользователя, событие другого типа, следующий день и пользователя с пропуском. Такой набор лучше ловит ошибку `count` вместо `nunique` и неправильный фильтр.

pythonПример ожидаемого результата
events = pd.DataFrame({
    'user_id': [1, 1, 2, 3, 3],
    'occurred_at': [
        '2026-08-01 10:00', '2026-08-01 10:05',
        '2026-08-01 11:00', '2026-08-01 12:00',
        '2026-08-02 09:00',
    ],
    'event_name': ['core_action', 'core_action', 'login', 'core_action', 'core_action'],
})

actual = calculate_dau(events)
expected = pd.DataFrame({
    'event_date': [pd.to_datetime('2026-08-01').date(), pd.to_datetime('2026-08-02').date()],
    'dau': [2, 1],
})
pd.testing.assert_frame_equal(actual.reset_index(drop=True), expected)

Что проверять кроме одного числа

Для метрики важны структура и инварианты. DAU не может быть больше числа уникальных пользователей в исходном срезе. Выручка после фильтра paid не должна стать отрицательной. Доля групп должна складываться в 100% с оговоркой на округление. Такие проверки переживают изменение конкретных значений.

Инварианты для аналитического расчёта
ОбъектПроверка
DAUне больше числа уникальных пользователей в периоде
Retentionот 0 до 1 и не выше размера стартовой когорты
Конверсияот 0 до 1, знаменатель не равен нулю
Выручканеотрицательна после явной обработки возвратов
Сегментыключи не пропали после merge, unknown виден отдельно

pytest делает тесты частью запуска

Когда функций становится больше, тесты удобно хранить отдельно и запускать одной командой. `pytest` покажет, какой сценарий сломался. Для аналитика особенно полезны читаемые имена: по названию должно быть ясно, какую бизнес-ошибку защищает тест.

pythonТест с понятным сценарием
def test_dau_counts_unique_users_per_day():
    events = make_events_for_two_days()

    result = calculate_dau(events)

    assert result.loc[result['event_date'].eq(date(2026, 8, 1)), 'dau'].item() == 2
    assert result['dau'].sum() == 3
Тест — не доказательство правильной метрики

Он защищает зафиксированное правило. Если само определение DAU изменилось, сначала обнови решение и документацию, а потом тест. Не меняй expected только ради зелёного CI.

Продолжить чтение
Вся библиотека