Add Clusters
Cluster paths using ML and add a new segment column with cluster_0, cluster_1,
etc. cluster labels.
Per-path metrics are computed from features, optionally scaled, then passed to
the chosen clustering algorithm. The resulting cluster label is broadcast to every
row of the corresponding path.
Usage
stream.add_clusters(
name="cluster",
features=[
{"metric": "length"},
{"metric": "event_count", "metric_args": {"event": "purchase"}},
],
method="kmeans",
method_args={"n_clusters": 4},
scaler="minmax",
)
# the same clustering the headless analysis settled on
result = stream.cluster_analysis_data(features=[{"metric": "length"}])
stream.add_clusters(name="cluster", features=[{"metric": "length"}], **result["best_params"])
How it works
add_clusters is the non-interactive half of
Cluster Analysis: same features, same scaler,
same algorithms, no UI. It computes the features metrics per path, scales
them, clusters the result, and writes the label back onto every row of the
corresponding path as a new segment column — after which the
clusters behave like any other segment, in diff mode, Segment Overview, or the
in_segment metric.
Two things are worth knowing before you call it.
method_args={"n_clusters": ...} is required for k-means, and that is not an
oversight. The
widget searches a range and picks a winner by silhouette score, because it can
show you the result and let you disagree. A processor writing a column into your
data has no such conversation, so it asks for the exact number rather than
guessing one. The intended flow is to settle the question in the widget first —
"Save Clusters" emits the matching call, and headlessly
cluster_analysis_data()["best_params"] carries the same value — already shaped
as the arguments of this call, so it splats straight in:
features = [{"metric": "length"}, {"metric": "active_days"}]
result = stream.cluster_analysis_data(features=features)
stream = stream.add_clusters("behavior", features=features, **result["best_params"])
Clustering is not deterministic across feature sets. Labels are positional
(cluster_0, cluster_1, …) and carry no meaning of their own — adding a
feature or changing the scaler can renumber every group. Rename them to
something you can read once the split is settled, with
rename_segment_levels:
stream = stream.rename_segment_levels("behavior", {"cluster_0": "browsers", "cluster_1": "buyers"})
See Path Metrics for what you can cluster on, and
Cluster Analysis for why the
choice of features is the analysis.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | str | Name of the new segment column to add. |
features | list of dict | Metric configurations used as clustering features. Each dict has a "metric" key (str) and an optional "metric_args" key (dict). Available metrics: "length", "duration", "event_count", "has_event", "event_count_bulk", "has_event_bulk", "has_all_events", "has_any_event", "time_between", "first_event_time", "active_days", "matches_pattern", "in_segment", "in_segment_bulk". See the Path Metrics documentation page for the full metric reference. |
method | str, default "kmeans" | Clustering algorithm. One of "kmeans" or "hdbscan". |
method_args | dict, optional | Parameters of the chosen method: n_clusters for "kmeans" (required), min_cluster_size and cluster_selection_epsilon for "hdbscan" (both optional). This is the metric / metric_args shape applied to algorithms, so a key that does not belong to method raises rather than being ignored. scaler and nmf_components are not method arguments — they are pipeline steps applied before clustering, so they stay outside this dict. |
scaler | str or None, default "minmax" | Feature scaler applied before clustering. One of "minmax", "std", or None. ("standard" is accepted as a legacy alias of "std".) |
nmf_components | int, optional | When set, reduces features to this many NMF components before clustering. |
path_col | str, optional | Path ID column override; defaults to schema.path_col. |