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