Cohort 4Register

P8 Sessionize events · P9 Consecutive streaks

FreeLesson 61 of 78

P8Sessionize eventsDEDAHARD

Sounds like: "split clickstream into sessions", "a session ends after 30 minutes idle", "average session length"

Worked question: Group each user's events into sessions, where a gap of more than 30 minutes starts a new session.

Approach: 1. group by user 2. sort by time 3. new session when the gap exceeds 30 minutes

SOLUTIONPYTHON 3.12
import json
from collections import defaultdict
from datetime import datetime, timedelta

GAP = timedelta(minutes=30)
by_user = defaultdict(list)
for line in open("events.jsonl"):
    e = json.loads(line)
    ts = datetime.fromisoformat(e["ts"])
    by_user[e["user_id"]].append((ts, e["event"]))

for user, events in sorted(by_user.items()):
    events.sort()                         # never trust input order
    sessions, prev = [], None
    for ts, ev in events:
        if prev is None or ts - prev > GAP:
            sessions.append([])           # start a new session
        sessions[-1].append(ev)
        prev = ts
    print(user, sessions)

Output

1 [['view', 'cart', 'checkout', 'purchase'], ['view']]
2 [['view', 'view'], ['cart']]
3 [['view', 'cart', 'checkout']]
4 [['view']]

P9Consecutive streaksDEDAMEDIUM

Sounds like: "longest login streak", "most consecutive days with sales", "longest consecutive sequence"

Worked question: Each user's longest run of consecutive login days.

Approach: 1. put dates in a set 2. only count from a day whose previous day is missing 3. walk forward while days exist

SOLUTIONPYTHON 3.12
import csv
from collections import defaultdict
from datetime import date, timedelta

ONE = timedelta(days=1)
days = defaultdict(set)
for r in csv.DictReader(open("logins.csv")):
    days[int(r["user_id"])].add(date.fromisoformat(r["login_date"]))

for user, ds in sorted(days.items()):
    best = (0, None)
    for d in ds:
        if d - ONE not in ds:             # d starts a streak
            n = 1
            while d + n * ONE in ds:
                n += 1
            best = max(best, (n, d))
    print(user, "longest:", best[0], "days from", best[1])

Output

1 longest: 3 days from 2025-04-01
2 longest: 2 days from 2025-04-01
3 longest: 4 days from 2025-04-03

Follow-ups interviewers ask

They askYou answer
"Average session length."Keep first and last timestamp per session and average the differences. A one-event session has length 0: say whether to exclude it.
"Streaks with gaps of one day allowed."Change the test: a day starts a streak only if neither of the two days before it exists.
"Events arrive out of order in a stream."Sort within a time window before sessionizing, and accept late events up to a cut-off (a watermark in Spark).

Practice before you move on

  1. Solve the two practice questions for P8 and P9 in Coding Practice, under Python.
  2. Change the session gap in P8 to 10 minutes and count the sessions again.

In Coding Practice

Solve this pattern on fresh data, under Python. Hidden tests grade it.