Skip to content

Clustering (segmentation)

Segment customers with ClusteringDetector, then read the cluster profiles and keywords. This example fuses structured features with a free-text column.

Install extras

pip install "parleml-ml4[unsupervised]" (scikit-learn clustering; UMAP for the 2-D map). Neural text fusion additionally uses the embeddings stack.

1. A mixed structured + text frame

import numpy as np
import pandas as pd

rng = np.random.default_rng(0)
n = 1_500
spend = np.concatenate([rng.normal(200, 40, n // 3),
                        rng.normal(800, 120, n // 3),
                        rng.normal(2500, 300, n - 2 * (n // 3))])
tenure = rng.integers(1, 60, n)
notes = rng.choice(
    ["price sensitive, coupons", "premium features, priority support",
     "enterprise contract, dedicated rep"], n)

df = pd.DataFrame({"monthly_spend": spend.round(2), "tenure_months": tenure, "notes": notes})

2. Fit — auto-k with text fusion

from parleml_ml4.unsupervised import ClusteringDetector
from parleml_ml4.config.clustering import ClusteringConfig
from parleml_ml4.config.unsupervised import UnsupervisedConfig

uns = UnsupervisedConfig(use_text_embeddings=True, text_columns=["notes"])

det = ClusteringDetector(
    ClusteringConfig(algorithm="kmeans", n_clusters="auto"),
    unsupervised_config=uns,
    outputs_dir="runs/segments",
).fit(df)

res = det.result_
print(res.n_clusters, "clusters,", res.n_noise, "noise points")
print(res.silhouette_features, res.davies_bouldin_features,
      res.calinski_harabasz_features)

With n_clusters="auto" every algorithm that takes a cluster count — kmeans, minibatch_kmeans, agglomerative and birch — sweeps a k range and keeps the best by silhouette; the full sweep lands on res.auto_k_sweep. GMM sweeps gmm_n_components by BIC/AIC instead. For density clustering use algorithm="hdbscan" (the default) or dbscan — those discover the cluster count from the data, so n_clusters does not apply.

3. Read the segments

labels = res.labels                     # cluster id per row (-1 = noise for density algos)
print(res.cluster_keywords)             # c-TF-IDF keywords per cluster (auto when text embedded)

cluster_keywords is the most reliable "what is this cluster" signal — the distinctive terms per segment, extracted automatically whenever text columns are embedded.

4. Score new rows (inductive algorithms only)

kmeans, minibatch_kmeans, gmm, and birch support predict; the transductive algorithms (dbscan, hdbscan, agglomerative) raise a clean NotImplementedError.

if det.is_inductive:
    new_labels = det.predict(df.head())          # assign new rows to learned clusters
    reasons    = det.explain_rows(df.head())     # cluster + distance + nearest other

5. Persist a deployable pipeline

With outputs_dir set, the run wrote events.ndjson / manifest.json and a deployable pipeline under runs/segments/deploy/. Build the same artefact in-memory with det.to_pipeline().

See the Unsupervised guide for the full algorithm roster, Embeddings for text fusion, and the ClusteringDetector reference.

Next: Anomaly detection · Unsupervised guide