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

ParameterTypeDescription
consecutivebool or list of str, optionalCollapse 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_groupslist of dict, optionalMerge a chain of events into a single representative event. See event_groups below for worked examples.
group_colstr, optionalGroup 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_colstr, optionalCollapse events within each session defined by this column. Requires session_type_col as well.
session_type_colstr, optionalColumn that distinguishes session event types (used with session_col).
aggdict, optionalAggregation 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_colstr, optionalPath ID column override; defaults to schema.path_col.
event_colstr, optionalEvent 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 a start_event and the next end_event, inclusive of both, into one group.
  • name (str) — label for the merged event, required unless cases are given.
  • cases (list of dict, optional) — conditional labels evaluated against the group's own events, falling back to name for 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_ideventtimestampprice
u1add_to_cart00:00:0010
u1add_to_cart00:00:0525
u1add_to_cart00:00:0915
u1purchase00:00:2050
stream.collapse_events(consecutive=True)  # no agg: price takes the first value
user_ideventtimestampprice
u1add_to_cart00:00:0010
u1purchase00:00:2050
stream.collapse_events(consecutive=True, agg={"price": "max"})
user_ideventtimestampprice
u1add_to_cart00:00:0025
u1purchase00:00:2050
stream.collapse_events(consecutive=True, agg={"price": "mean"})
user_ideventtimestampprice
u1add_to_cart00:00:0016.67
u1purchase00:00:2050