deepbox/ml
Clustering
Partitioning, density-based, hierarchical, graph-based and mixture-model clustering algorithms.
- Unsupervised
Overview
- Fit with
fit(X)and read the assignments from thelabelsgetter, or callfitPredict(X). Every clusterer also haspredict.DBSCANlabels noise points -1. - Defaults:
KMeansuses 8 clusters, k-means++ initialization andnInit: 10restarts.DBSCANuseseps: 0.5andminSamples: 5.AgglomerativeClusteringuses 2 clusters with Ward linkage.SpectralClusteringuses 8 clusters and anrbfaffinity.GaussianMixtureuses 1 component andcovarianceType: "diag", where scikit-learn uses"full". - Pass
randomStatefor repeatable results.KMeansalso exposesclusterCentersandinertia. - Clustering is sensitive to feature scale, so standardize features first.
AffinityPropagation
Affinity Propagation clustering algorithm.
Follows the message-passing scheme of Frey and Dueck (2007) as implemented in scikit-learn: similarities are negative squared Euclidean distances, the diagonal of the similarity matrix holds the preference (default: the median of all similarities, diagonal included), and tiny random noise breaks exact ties. Samples with a positive a(k,k) + r(k,k) become exemplars.
The algorithm needs three n-by-n matrices, so memory grows quadratically with the number of samples. If no exemplar emerges, all labels are -1 and a ConvergenceWarning is issued.
import { AffinityPropagation } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[0, 0], [0.1, 0], [10, 10], [10.1, 10]]);const ap = new AffinityPropagation({ damping: 0.5 });ap.fit(X);console.log(ap.labels);Members (11)
constructor(options?: { readonly damping?: number; readonly maxIter?: number; readonly convergenceIter?: number; readonly preference?: number; readonly affinity?: Affinity; readonly randomState?: number; })Create an Affinity Propagation model.
labels: TensorCluster labels of the training samples (-1 for all samples if no exemplar was found).
clusterCenters: TensorExemplar samples, one per cluster.
clusterCentersIndices: Int32ArrayRow indices of the exemplars in the training data (sorted, one per cluster).
nIter: numberNumber of iterations that were run.
converged: booleanWhether the exemplar set stabilized before
maxIterwas reached.fit(X: Tensor, _y?: Tensor): thisRun affinity propagation.
predict(X: Tensor): TensorAssign each sample to the nearest cluster center (exemplar).
fitPredict(X: Tensor, _y?: Tensor): TensorFit the model and return the cluster labels of the training samples.
getParams(): Record<string, unknown>Get hyperparameters for this estimator.
setParams(params: Record<string, unknown>): thisSet the parameters of this estimator.
AgglomerativeClustering
Agglomerative (hierarchical) clustering.
Bottom-up clustering that starts with each sample as its own cluster and iteratively merges the closest pair of clusters until nClusters clusters remain (or, with distanceThreshold, until the closest pair is at least that far apart).
Supported linkage criteria: - single: minimum distance between clusters - complete: maximum distance between clusters - average: average distance between clusters - ward: minimizes the total within-cluster variance (Euclidean only)
Distances are Euclidean. The dendrogram is built in O(n^2) time and O(n^2) memory, so very large inputs (tens of thousands of samples) are not practical.
import { AgglomerativeClustering } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[1, 2], [1.5, 1.8], [5, 8], [8, 8]]);const agg = new AgglomerativeClustering({ nClusters: 2 });agg.fit(X);console.log(agg.labels);Members (12)
constructor(options?: { readonly nClusters?: number | null; readonly linkage?: Linkage; readonly distanceThreshold?: number; })Create an agglomerative clustering model.
labels: TensorCluster labels of the training samples.
clusterCenters: TensorCentroid (mean of the training samples) of every cluster, indexed by label.
nClusters: numberNumber of clusters found.
nLeaves: numberNumber of training samples (leaves of the dendrogram).
children: TensorChildren of every non-leaf node of the dendrogram.
distances: TensorDistance at which every merge of the dendrogram happened (for ward linkage, the Ward merge height sqrt(2 * n_a * n_b / (n_a + n_b)) * ||c_a - c_b||).
fit(X: Tensor, _y?: Tensor): thisBuild the cluster hierarchy of X and cut it.
predict(X: Tensor): TensorPredict cluster labels for new samples using nearest-neighbor assignment.
fitPredict(X: Tensor, _y?: Tensor): TensorFit the model and return the cluster labels of the training samples.
getParams(): Record<string, unknown>Get hyperparameters for this estimator.
setParams(params: Record<string, unknown>): thisSet the parameters of this estimator.
Birch
BIRCH clustering algorithm.
Phase 1: Build a CF tree by scanning the data. Each sample joins the closest leaf entry if the merged entry keeps a radius (root mean squared distance of its members to their centroid) of at most threshold; otherwise it starts a new entry. A node with more than branchingFactor entries is split. Phase 2: Apply Ward agglomerative clustering on the leaf entries to produce final nClusters clusters. With nClusters: null this step is skipped and every leaf entry is its own cluster.
The tree is built on mean-centered data, so large constant offsets in X do not degrade the radius test. Samples are labeled by their closest leaf entry.
import { Birch } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[0, 0], [0.1, 0], [10, 10], [10.1, 10]]);const birch = new Birch({ nClusters: 2, threshold: 0.5 });birch.fit(X);console.log(birch.labels);Members (11)
constructor(options?: { readonly nClusters?: number | null; readonly threshold?: number; readonly branchingFactor?: number; })Create a BIRCH model.
labels: TensorCluster labels of the samples passed to the last
fitorpartialFitcall.clusterCenters: TensorCenters of the final clusters: the sample-count weighted mean of their leaf entries.
subclusterCenters: TensorCentroids of the leaf entries of the CF tree.
subclusterLabels: TensorFinal cluster of every leaf entry.
fit(X: Tensor, _y?: Tensor): thisBuild the CF tree from X (discarding any previous tree) and cluster its leaf entries.
partialFit(X?: Tensor, _y?: Tensor): thisAdd the samples of X to the existing CF tree (or start one) and recompute the global clustering.
predict(X: Tensor): TensorAssign each sample the label of its closest leaf entry.
fitPredict(X: Tensor, _y?: Tensor): TensorFit the model and return the cluster labels of the training samples.
getParams(): Record<string, unknown>Get hyperparameters for this estimator.
setParams(params: Record<string, unknown>): thisSet the parameters of this estimator.
DBSCAN
DBSCAN (Density-Based Spatial Clustering of Applications with Noise).
Clusters points based on density. Points in high-density regions are grouped together, while points in low-density regions are marked as noise.
Algorithm: 1. For each point, find all neighbors within eps distance (the point itself counts) 2. If a point has at least minSamples neighbors, it's a core point 3. Core points and their neighbors form clusters 4. Points not reachable from any core point are noise (label = -1)
A border point that lies within eps of core points from several clusters joins the cluster that reaches it first, so labels of border points depend on the order of the rows in X.
Advantages: - No need to specify number of clusters - Can find arbitrarily shaped clusters - Points in sparse regions are labeled as noise instead of distorting a cluster
import { DBSCAN } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[1, 2], [2, 2], [2, 3], [8, 7], [8, 8], [25, 80]]);const dbscan = new DBSCAN({ eps: 3, minSamples: 2 });const labels = dbscan.fitPredict(X);// labels: [0, 0, 0, 1, 1, -1] (-1 = noise)Members (9)
constructor(options?: { readonly eps?: number; readonly minSamples?: number; readonly metric?: "euclidean" | "manhattan"; })Create a DBSCAN model.
labels: TensorGet cluster labels assigned during fitting.
nClusters: numberNumber of clusters found (excluding noise).
coreIndices: number[]Get indices of core samples discovered during fitting.
fit(X: Tensor, _y?: Tensor): thisPerform DBSCAN clustering on data X.
predict(X: Tensor): TensorPredict cluster labels for new samples using nearest-neighbor assignment.
fitPredict(X: Tensor, _y?: Tensor): TensorFit DBSCAN and return cluster labels.
getParams(): Record<string, unknown>Get hyperparameters for this estimator.
setParams(params: Record<string, unknown>): thisSet the parameters of this estimator.
GaussianMixture
Gaussian Mixture Model (GMM).
Fits a mixture of Gaussian distributions using the Expectation-Maximization (EM) algorithm. The covariance structure is chosen with covarianceType: "diag" (the default, one variance per feature and component), "spherical" (one variance per component), "full" (a full matrix per component) or "tied" (one full matrix shared by all components).
Unlike scikit-learn, whose default is "full", the default here stays "diag" so that results of earlier Deepbox versions are unchanged.
Iteration stops when the change of the mean log-likelihood per sample falls below tol. The run with the best log-likelihood out of nInit starts is kept.
import { GaussianMixture } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[1, 2], [1.5, 1.8], [5, 8], [8, 8], [1, 0.6], [9, 11]]);const gmm = new GaussianMixture({ nComponents: 2 });gmm.fit(X);const labels = gmm.predict(X);Members (19)
constructor(options?: { readonly nComponents?: number; readonly maxIter?: number; readonly tol?: number; readonly nInit?: number; readonly regCovar?: number; readonly randomState?: number; readonly covarianceType?: CovarianceType; readonly initParams?: InitParams; })Create a Gaussian mixture model.
labels: TensorComponent labels of the training samples.
clusterCenters: TensorComponent means.
means: TensorComponent means (same as
clusterCenters).weights: TensorMixing weights of the components; they sum to 1.
covariances: TensorCovariances of the components.
converged: booleanWhether EM reached the
tolthreshold in the best run.nIter: numberNumber of EM iterations of the best run.
lowerBound: numberMean log-likelihood per sample reached by the best run, evaluated before its last M-step.
fit(X: Tensor, _y?: Tensor): thisEstimate the model parameters with the EM algorithm.
predict(X: Tensor): TensorPredict the most probable component for each sample.
fitPredict(X: Tensor, _y?: Tensor): TensorFit the model and return the component labels of the training samples.
predictProba(X: Tensor): TensorPosterior probability of each component for each sample.
scoreSamples(X: Tensor): TensorLog-likelihood of each sample under the model.
score(X: Tensor): numberMean log-likelihood of the samples under the model.
bic(X: Tensor): numberBayesian information criterion of the model on X (lower is better).
aic(X: Tensor): numberAkaike information criterion of the model on X (lower is better).
getParams(): Record<string, unknown>Get hyperparameters for this estimator.
setParams(params: Record<string, unknown>): thisSet the parameters of this estimator.
KMeans
K-Means clustering algorithm.
Partitions n samples into k clusters by minimizing the within-cluster sum of squared distances to cluster centroids.
Algorithm: Lloyd's algorithm (iterative refinement), optionally with Elkan's triangle-inequality bounds 1. Initialize k centroids (random or greedy k-means++) 2. Assign each point to nearest centroid 3. Update centroids as mean of assigned points (empty clusters are moved to the points that are farthest from their centroid) 4. Stop when the assignment no longer changes, when the squared centroid shift is at most tol * mean(var(X)), or after maxIter iterations
tol is relative to the average per-feature variance of X, like scikit-learn, so the stopping rule does not depend on the scale of the data.
Time Complexity: O(n * k * i * d) where: - n = number of samples - k = number of clusters - i = number of iterations - d = number of features
import { KMeans } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[1, 2], [1.5, 1.8], [5, 8], [8, 8], [1, 0.6], [9, 11]]);const kmeans = new KMeans({ nClusters: 2, randomState: 42 });kmeans.fit(X); const labels = kmeans.predict(X);console.log('Cluster labels:', labels);console.log('Centroids:', kmeans.clusterCenters);Members (15)
constructor(options?: { readonly nClusters?: number; readonly maxIter?: number; readonly tol?: number; readonly init?: "random" | "kmeans++" | "k-means++"; readonly nInit?: number; readonly warmStart?: boolean; readonly randomState?: number; readonly algorithm?: "lloyd" | "elkan" | "auto"; })Create a new K-Means clustering model.
clusterCenters: TensorGet cluster centers.
labels: TensorGet training labels.
inertia: numberGet inertia (sum of squared distances of samples to their closest centroid).
nIter: numberGet number of iterations run by the best run.
nFeaturesIn: numberNumber of features seen during fit.
fit(X: Tensor, _y?: Tensor): thisFit K-Means clustering on training data.
predict(X: Tensor): TensorPredict the closest cluster for each sample.
fitPredict(X: Tensor, _y?: Tensor): TensorFit and predict in one step.
transform(X: Tensor): TensorTransform X to cluster-distance space.
fitTransform(X: Tensor, _y?: Tensor): TensorFit the model and return the distances of the training samples to every centroid.
score(X: Tensor): numberOpposite of the inertia of
Xunder the fitted centroids (higher is better).getParams(): Record<string, unknown>Get hyperparameters for this estimator.
setParams(params: Record<string, unknown>): thisSet the parameters of this estimator.
clone(): KMeansCreate an unfitted copy with the same hyperparameters.
MeanShift
Mean Shift clustering with a flat kernel.
Automatically determines the number of clusters from the density landscape of the data. The bandwidth parameter is the radius of the kernel. With bandwidth: "auto" it is estimated from the data the same way as sklearn.cluster.estimate_bandwidth with quantile=0.3.
Clusters are ordered by the number of samples they attract (strongest mode first), so label 0 is always the densest mode.
import { MeanShift } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[0, 0], [0.5, 0], [10, 10], [10.5, 10]]);const ms = new MeanShift({ bandwidth: 2 });ms.fit(X);console.log(ms.labels);console.log(ms.clusterCenters);Members (9)
constructor(options?: { readonly bandwidth?: number | "auto"; readonly maxIter?: number; readonly tol?: number; readonly binSeeding?: boolean; readonly minBinFreq?: number; readonly clusterAll?: boolean; })Create a new Mean Shift model.
labels: TensorTraining labels of shape (n_samples,); -1 marks samples left unclustered when
clusterAllis false.clusterCenters: TensorCluster centers of shape (n_clusters, n_features), strongest mode first.
nIter: numberLargest number of shift iterations any seed needed.
fit(X: Tensor, _y?: Tensor): thisFit Mean Shift clustering.
predict(X: Tensor): TensorAssign each sample to the nearest cluster center found during
fit.fitPredict(X: Tensor, _y?: Tensor): TensorFit the model and return the training labels.
getParams(): Record<string, unknown>setParams(params: Record<string, unknown>): this
MiniBatchKMeans
Mini-Batch K-Means clustering.
A faster variant of KMeans that updates the centroids from small random batches of data (Sculley, 2010): each batch sample is assigned to its nearest centroid, then every centroid moves towards its samples with a per-centroid learning rate of 1 / (number of samples it has seen), so a centroid is the running mean of all samples assigned to it. This trades a small amount of quality for much lower cost on large datasets.
maxIter counts mini-batch steps (one batch per step), not passes over the data as in scikit-learn. Training also stops early when the smoothed batch inertia has not improved for maxNoImprovement consecutive steps, or when the squared centroid movement of a step is at most tol * mean(var(X)) (disabled for the default tol: 0). With nInit > 1 the whole procedure is repeated and the run with the lowest inertia on the full data is kept.
import { MiniBatchKMeans } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[1, 2], [1.5, 1.8], [5, 8], [8, 8], [1, 0.6], [9, 11]]);const km = new MiniBatchKMeans({ nClusters: 2, batchSize: 3, randomState: 42 });km.fit(X);console.log(km.clusterCenters);Members (14)
constructor(options?: { readonly nClusters?: number; readonly maxIter?: number; readonly batchSize?: number; readonly tol?: number; readonly nInit?: number; readonly init?: "random" | "kmeans++"; readonly randomState?: number; readonly maxNoImprovement?: number; })Create a new Mini-Batch K-Means model.
clusterCenters: TensorCluster centers of shape (n_clusters, n_features).
labels: TensorLabels of the training data (of the latest batch after
partialFit).inertia: numberSum of squared distances of the samples to their closest centroid.
nSteps: numberNumber of mini-batch steps performed by the selected run (plus any
partialFitcalls).fit(X: Tensor, _y?: Tensor): thisFit Mini-Batch K-Means on training data.
partialFit(X: Tensor, _y?: Tensor): thisUpdate the model with a single mini-batch (online learning).
predict(X: Tensor): TensorPredict the closest cluster for each sample.
fitPredict(X: Tensor, _y?: Tensor): TensorFit the model and return the training labels.
transform(X: Tensor): TensorTransform X to cluster-distance space.
fitTransform(X: Tensor, _y?: Tensor): TensorFit the model and return the distances of the training samples to every centroid.
score(X: Tensor): numberOpposite of the inertia of
Xunder the fitted centroids (higher is better).getParams(): Record<string, unknown>setParams(params: Record<string, unknown>): this
OPTICS
OPTICS clustering algorithm.
Computes the core distances, the reachability distances and the visiting order of every sample (Euclidean metric), then extracts flat clusters from the reachability plot with one of two methods:
- "dbscan" (default): cut the plot at eps, like DBSCAN with that radius. When eps is not given it defaults to maxEps; if maxEps is infinite, the 75th percentile of the finite reachability distances is used instead (a Deepbox heuristic; scikit-learn would put everything in one cluster). - "xi": the Xi-steep method of the paper, with predecessor correction, as in sklearn.cluster.OPTICS(cluster_method="xi"). Cluster boundaries are the steep regions of the plot whose reachability ratio is at least xi.
Noise samples get label -1. As in scikit-learn, minSamples counts the sample itself, and samples are visited in index order, with ties in reachability broken by the lower index.
Distances are computed on the fly, so memory use is linear in the number of samples; run time is quadratic.
import { OPTICS } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[0, 0], [0.1, 0], [10, 10], [10.1, 10]]);const optics = new OPTICS({ minSamples: 2 });optics.fit(X);console.log(optics.labels);console.log(optics.reachability);Members (13)
constructor(options?: { readonly minSamples?: number; readonly maxEps?: number; readonly clusterMethod?: "xi" | "dbscan"; readonly xi?: number; readonly eps?: number; readonly minClusterSize?: number; readonly predecessorCorrection?: boolean; })Create a new OPTICS model.
labels: TensorTraining labels of shape (n_samples,); -1 marks noise.
reachability: Float64ArrayReachability distance of every sample (indexed by sample,
Infinityfor unreachable ones).ordering: Int32ArraySample indices in the order OPTICS visited them.
coreDistances: Float64ArrayCore distance of every sample;
Infinityfor samples that are not core points withinmaxEps.predecessor: Int32ArrayFor every sample, the sample it was reached from (-1 for samples that start a new region).
clusterHierarchy: ReadonlyArray<readonly [ number, number ]>Clusters found by the 'xi' method as inclusive
[start, end]positions inordering, with nested clusters listed before the clusters that contain them.clusterCenters: TensorMean of every cluster found, shape (n_clusters, n_features); noise is excluded.
fit(X: Tensor, _y?: Tensor): thisCompute the OPTICS ordering and extract clusters.
predict(X: Tensor): TensorPredict cluster labels for new samples using nearest-neighbor assignment.
fitPredict(X: Tensor, _y?: Tensor): TensorFit the model and return the training labels.
getParams(): Record<string, unknown>setParams(params: Record<string, unknown>): this
SpectralClustering
Spectral Clustering using graph Laplacian eigenvectors.
Particularly effective for non-convex clusters and clusters with complex shapes that KMeans cannot handle well.
Affinities: - "rbf": exp(-gamma * ||x_i - x_j||^2) between all pairs (default). - "nearest_neighbors": each sample is linked to its nNeighbors nearest samples, counting itself as the first (so it has nNeighbors - 1 neighbors, as in scikit-learn); the graph is symmetrized as (C + C^T) / 2. - "precomputed": X is itself a square, non-negative affinity matrix; it is symmetrized as (A + A^T) / 2 and the diagonal is ignored.
The method builds and decomposes a dense n x n matrix, so memory is O(n^2) and time O(n^3).
import { SpectralClustering } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[0, 0], [0.1, 0], [10, 10], [10.1, 10]]);const sc = new SpectralClustering({ nClusters: 2 });sc.fit(X);console.log(sc.labels);Members (8)
constructor(options?: { readonly nClusters?: number; readonly affinity?: Affinity; readonly gamma?: number; readonly nNeighbors?: number; readonly randomState?: number; readonly nInit?: number; readonly nComponents?: number; })Create a new Spectral Clustering model.
labels: TensorTraining labels of shape (n_samples,).
clusterCenters: TensorMean of the training samples of every non-empty cluster in label order, shape (n_clusters, n_features).
fit(X: Tensor, _y?: Tensor): thisFit Spectral Clustering.
predict(X: Tensor): TensorPredict cluster labels for new samples using nearest-neighbor assignment.
fitPredict(X: Tensor, _y?: Tensor): TensorFit the model and return the training labels.
getParams(): Record<string, unknown>setParams(params: Record<string, unknown>): this
import { AgglomerativeClustering, DBSCAN, GaussianMixture, KMeans, SpectralClustering,} from "deepbox/ml";import { tensor } from "deepbox/ndarray"; const X = tensor([[1, 1], [1.2, 0.8], [4, 4], [4.2, 4.1]]); const kmeans = new KMeans({ nClusters: 2, randomState: 0 }).fit(X);console.log(kmeans.labels.toString(), kmeans.clusterCenters.toString(), kmeans.inertia);console.log(kmeans.predict(tensor([[0.9, 1.1]])).toString());console.log(new DBSCAN({ eps: 0.6, minSamples: 2 }).fit(X).labels.toString());console.log(new AgglomerativeClustering({ nClusters: 2 }).fit(X).labels.toString());console.log(new GaussianMixture({ nComponents: 2, covarianceType: "full", randomState: 0 }).fit(X).predict(X).toString());console.log(new SpectralClustering({ nClusters: 2, randomState: 0 }).fit(X).labels.toString());Found a mistake? Open an issue.