Collapse Events
Merge consecutive or grouped events into a single representative event.
Exactly one of consecutive, event_groups, group_col, or
session_col must be provided.
Usage
# Collapse any run of the same event
stream.collapse_events(consecutive=True)
# Collapse only repeated page_view events
stream.collapse_events(consecutive=["page_view"])
# Merge checkout steps into a single "checkout" event
stream.collapse_events(event_groups=[{"events": ["checkout_start", "checkout_step", "checkout_confirm"], "name": "checkout"}])
Parameters
| Parameter | Type | Description |
|---|---|---|
consecutive | bool or list of str, optional | Collapse consecutive repeats of the same event into one. Pass True to collapse all events; pass a list of event names to collapse only those specific events. |
event_groups | list of dict, optional | Merge a chain of events into a single representative event. See event_groups below for worked examples. |
group_col | str, optional | Group consecutive rows by this column's value: each run of rows sharing the same value is collapsed into one event named after that value. Example: a session_type column with values browse, browse, search collapses the path into browse -> search events. |
session_col | str, optional | Collapse events within each session defined by this column. Requires session_type_col as well. |
session_type_col | str, optional | Column that distinguishes session event types (used with session_col). |
agg | dict, optional | Aggregation rules for non-event columns when rows are merged, as a {column: agg_func} dict. agg_func is one of "first" (default), "last", "min", "max", "mean", "mode", "any". See agg below. Example: {"price": "max"}. |
path_col | str, optional | Path ID column override; defaults to schema.path_col. |
event_col | str, optional | Event column override; defaults to schema.event_col. |
Examples
Toy paths showing exactly what happens to the eventstream for each collapsing mode.
consecutive
Collapses each run of the same event into one row, keeping the run's first timestamp.
Before: page_view → page_view → page_view → click → click → product_view
stream.collapse_events(consecutive=True)
After — every run collapses, regardless of which event it is:
page_view → click → product_view
stream.collapse_events(consecutive=["page_view"])
After — only page_view runs collapse; the two click events are left alone
since click isn't in the list:
page_view → click → click → product_view
event_groups
Available keys:
events(str or list of str) — collapse any run of these events, wherever it occurs in the path, into one group.separator(str or list of str) — collapse every event up to and including the next separator event into one group.start_event+end_event(str or list of str) — collapse every event between astart_eventand the nextend_event, inclusive of both, into one group.name(str) — label for the merged event, required unlesscasesare given.cases(list of dict, optional) — conditional labels evaluated against the group's own events, falling back tonamefor groups no case matched.
event_groups merges a set of related events into one representative
event, named with a help of the name or cases additional arguments.
Which events belong to a group is decided by exactly one
of 3 boundary modes per group dict: events, separator, or start_event +
end_event.
Mode 1: events
Any run of the listed events collapses into one group, wherever it occurs — the events don't need to be adjacent to each other in the parameter, just adjacent in the path. Events not in the list pass through unchanged and end a run.
Before: home → payment_details → shipping_details → review_page → purchase
stream.collapse_events(event_groups=[
{"events": ["payment_details", "shipping_details", "review_page"], "name": "checkout"},
])
After: home → checkout → purchase
Mode 2: separator
A session is every event up to and including the next separator event
(inclusive of the separator itself). Trailing events after the last
separator in a path aren't part of any session and pass through unchanged.
Before: search → search_submit → browse → search → search_submit → purchase
stream.collapse_events(event_groups=[
{"separator": "search_submit", "name": "search_session"},
])
After — two separate search_session groups (each ending at its own
search_submit); the trailing purchase has no separator after it, so it's
left uncollapsed:
search_session → search_session → purchase
Mode 3: start_event + end_event
A session is every event between a start_event and the next end_event,
inclusive of both.
Before: home → checkout_start → payment_details → checkout_complete → confirmation_page
stream.collapse_events(event_groups=[
{"start_event": "checkout_start", "end_event": "checkout_complete", "name": "checkout"},
])
After: home → checkout → confirmation_page
cases
cases names a group differently depending on what happened inside it —
each case is a Filter Paths-style
condition, evaluated against only the group's own events. The group-level
name is the fallback for groups no case matched. cases is orthogonal to
the boundary mode — it can be combined with any of events, separator, or
start_event/end_event above; the example below just picks
start_event/end_event for concreteness.
Before — two independent checkout sessions (same boundary mode as
start_event/end_event above), one that reached purchase and one that
didn't:
- user 1:
checkout_start → payment_details → purchase → checkout_end - user 2:
checkout_start → payment_details → checkout_end
stream.collapse_events(event_groups=[{
"start_event": "checkout_start",
"end_event": "checkout_end",
"cases": [
{
"condition": {"op": "=", "metric": "has_event", "value": True,
"metric_args": {"event": "purchase"}},
"name": "successful_checkout",
},
],
"name": "abandoned_checkout", # fallback for groups the case didn't match
}])
After — the label depends on whether purchase occurred inside each group:
- user 1:
successful_checkout - user 2:
abandoned_checkout
multiple groups
event_groups is a list — each group dict is applied in order, and every
group after the first sees the output of the ones before it, not the
original path. A later group can therefore match against an event name
created by an earlier group.
Before: payment_details → shipping_details → review_page → purchase
stream.collapse_events(event_groups=[
{"events": ["payment_details", "shipping_details"], "name": "checkout_details"},
{"events": ["checkout_details", "review_page"], "name": "checkout"},
])
After — the first group merges payment_details/shipping_details into
checkout_details; the second group then merges that checkout_details
event together with review_page (which it can only match because the first
group already ran) into a single checkout:
checkout → purchase
group_col
Collapses each run of consecutive rows sharing the same value of group_col
into one event, named after that value.
Before: home → catalog → search → add_to_cart → purchase, with
screen_type = browse, browse, browse, action, action
stream.collapse_events(group_col="screen_type")
After — three browse rows collapse into one browse event, and the two
action rows collapse into one action event:
browse → action
session_col + session_type_col
Collapses every session (identified by session_col) into a single event
named after that session's session_type_col value.
Before: home → catalog → add_to_cart → purchase, with session_id =
s1, s1, s2, s2 and session_kind = browsing_session, browsing_session, buying_session, buying_session
stream.collapse_events(session_col="session_id", session_type_col="session_kind")
After — each session becomes a single event named after its type, at the session's first timestamp:
browsing_session → buying_session
agg
By default, every non-event column takes its first value within the
merged rows (ordered by timestamp). agg overrides this per column with one
of "first", "last", "min", "max", "mean", "mode", "any".
Before — a price column alongside three consecutive add_to_cart events:
| user_id | event | timestamp | price |
|---|---|---|---|
| u1 | add_to_cart | 00:00:00 | 10 |
| u1 | add_to_cart | 00:00:05 | 25 |
| u1 | add_to_cart | 00:00:09 | 15 |
| u1 | purchase | 00:00:20 | 50 |
stream.collapse_events(consecutive=True) # no agg: price takes the first value
| user_id | event | timestamp | price |
|---|---|---|---|
| u1 | add_to_cart | 00:00:00 | 10 |
| u1 | purchase | 00:00:20 | 50 |
stream.collapse_events(consecutive=True, agg={"price": "max"})
| user_id | event | timestamp | price |
|---|---|---|---|
| u1 | add_to_cart | 00:00:00 | 25 |
| u1 | purchase | 00:00:20 | 50 |
stream.collapse_events(consecutive=True, agg={"price": "mean"})
| user_id | event | timestamp | price |
|---|---|---|---|
| u1 | add_to_cart | 00:00:00 | 16.67 |
| u1 | purchase | 00:00:20 | 50 |