События пользователя в сессии: разрывы времени в pandas

В логе есть клики пользователя, но нет номера визита. Для анализа пути нужно объединить соседние события, пока между ними не прошло больше тридцати минут. Если считать разности по всему файлу, граница одного пользователя начнёт зависеть от другого.

Определить правило сессии

Сессия начинается с первого события пользователя или после паузы строго больше тридцати минут. Ровно тридцать минут остаются в прежней сессии. Это договор анализа, а не свойство браузера: другое правило даст другой результат. Запишите также, какие события участвуют в расчёте. Фоновый технический запрос способен искусственно продлить визит.

Сначала отсортируйте события внутри пользователя по времени. Для равных времён полезен стабильный event_id, если дальнейший порядок действий влияет на воронку. Повтор доставки одного события удаляют по настоящему ключу события, а не по времени.

Превратить разрывы в номера

diff внутри группы даёт расстояние от предыдущего события. Первый элемент имеет отсутствующий разрыв и начинает сессию. Булева отметка старта превращается в возрастающий номер через cumsum, также внутри пользователя. Итоговый ключ состоит из user и session: номер 1 повторяется у разных людей.

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

import pandas as pd
x = pd.DataFrame({"user": ["A", "A", "A", "B"], "event_id": [1, 2, 3, 4],
 "time": pd.to_datetime(["2026-10-01 10:00", "2026-10-01 10:30", "2026-10-01 11:01", "2026-10-01 10:10"])})
x = x.sort_values(["user", "time", "event_id"])
gap = x.groupby("user")["time"].diff()
x["start"] = gap.isna() | gap.gt(pd.Timedelta(minutes=30))
x["session"] = x["start"].groupby(x["user"]).cumsum()
report = x.groupby(["user", "session"]).agg(events=("event_id", "size"),
              first=("time", "min"), last=("time", "max"))
print(report)
assert x.loc[x["user"].eq("A"), "session"].tolist() == [1, 1, 2]
assert len(report) == 3 and report["events"].sum() == len(x)

У A две сессии: первые два события вместе, третье отдельно. У B своя первая сессия. Все четыре события сохранились. Длительность между first и last описывает наблюдаемый интервал действий; одиночная сессия имеет нулевую наблюдаемую длительность.

Не обрезать контекст на границе файла

Если обрабатывать дни отдельно, первое событие после полуночи может продолжать вечернюю сессию. Подгрузите предыдущее событие каждого пользователя либо храните состояние между порциями. Иначе количество сессий будет зависеть от границы выгрузки.

Анонимный id, который меняется после входа, тоже разрывает историю. Решите, есть ли подтверждённая таблица соответствий идентификаторов. Склеивать людей по похожему времени нельзя. Отсутствующий пользователь должен иметь отдельную политику, иначе все неизвестные события образуют одного огромного пользователя.

После расчёта сохраните ключ сессии рядом с event_id. Тогда воронка, число просмотренных страниц и длительность используют одну сегментацию. Если каждая витрина заново вычисляет визиты по своему набору событий, расхождения станут частью определения, а не ошибкой суммирования.

Закрепить на практике

В бесплатном уроке groupby и transform вы научитесь добавлять групповые значения на уровень строк. Это основа для работы с пользовательскими событиями; diff и накопление границ из статьи можно разобрать следующим шагом. Откройте бесплатный урок и задачи. Полная последовательность тем — в бесплатном курсе.

Продолжить на практике

  • NumPy и pandas: практический тренажёр ↗

    Бесплатный тренажёр NumPy и pandas для тех, кто знает основы Python. Научитесь очищать таблицы, объединять данные и собирать отчёты: 31 урок и 124 задачи с кодом и автоматической проверкой.