To Daily States
Convert the eventstream into daily lifecycle-state events.
Each path is expanded to one row per calendar day from its first event
to max_dormant_days days after its last event. Every row is labelled
with one of six engagement states.
Active days:
new — first-ever active day for this path
current — active within the past 7 days
reactivated — active 8-30 days ago, not in the last 7
resurrected — last active more than 30 days ago
Inactive days:
at_risk_wau — was active within the past 7 days
at_risk_mau — was active 8-30 days ago
dormant — was last active more than 30 days ago
Usage
stream.to_daily_states()
stream.to_daily_states(active_events=["purchase", "add_to_cart"], max_dormant_days=60)
How it works
This processor changes what a "step" in a path means. Normally the next event in
a path is the next thing the user did, whenever that was. After
to_daily_states, every path has exactly one event per calendar day — the
user's engagement state on that day — so consecutive steps are consecutive days
and the gaps become visible as events in their own right.
Before — one user, active on two days:
| user_id | event | timestamp |
|---|---|---|
| u1 | login | 2024-01-01 |
| u1 | login | 2024-01-03 |
stream.to_daily_states()
After — one row per day, labelled with that day's state:
| user_id | event | timestamp |
|---|---|---|
| u1 | new | 2024-01-01 |
| u1 | at_risk_wau | 2024-01-02 |
| u1 | current | 2024-01-03 |
The first-ever active day is new; 2 January has no activity but the user was
active within the last 7 days, so it is at_risk_wau; on 3 January the user is
active again and recently seen, so current.
Rows keep being generated for max_dormant_days after the path's last event, so
you can see a path decay through at_risk_wau → at_risk_mau → dormant
without it silently disappearing from the data. That tail is capped at the last
day present in the dataset, so paths active near the end of your observation
window get fewer trailing rows than older ones — worth remembering before
comparing state distributions across cohorts.
Pass active_events to decide what counts as activity: with
active_events=["purchase"], a user who browses daily but never buys still
decays to dormant.
Because the output is an ordinary eventstream whose events are states, every tool works on it unchanged — a Transition Graph becomes a state-transition diagram, and a Step Sankey becomes a retention flow.
Parameters
| Parameter | Type | Description |
|---|---|---|
active_events | list of str, optional | Events that count as "activity". If omitted, any event counts. |
max_dormant_days | int, default 30 | Days after a path's last event to continue generating state rows. Capped at the last day present in the dataset, so paths active near the end of the observation window get fewer trailing rows. |
agg | dict, optional | Per-column aggregation overrides (e.g. {"revenue": "sum"}). |
path_col | str, optional | Override the path ID column. |
event_col | str, optional | Override the event column. |