Skip to content

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 the labels getter, or call fitPredict(X). Every clusterer also has predict. DBSCAN labels noise points -1.
  • Defaults: KMeans uses 8 clusters, k-means++ initialization and nInit: 10 restarts. DBSCAN uses eps: 0.5 and minSamples: 5. AgglomerativeClustering uses 2 clusters with Ward linkage. SpectralClustering uses 8 clusters and an rbf affinity. GaussianMixture uses 1 component and covarianceType: "diag", where scikit-learn uses "full".
  • Pass randomState for repeatable results. KMeans also exposes clusterCenters and inertia.
  • Clustering is sensitive to feature scale, so standardize features first.

Classes

10

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.

Example
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: Tensor

    Cluster labels of the training samples (-1 for all samples if no exemplar was found).

  • clusterCenters: Tensor

    Exemplar samples, one per cluster.

  • clusterCentersIndices: Int32Array

    Row indices of the exemplars in the training data (sorted, one per cluster).

  • nIter: number

    Number of iterations that were run.

  • converged: boolean

    Whether the exemplar set stabilized before maxIter was reached.

  • fit(X: Tensor, _y?: Tensor): this

    Run affinity propagation.

  • predict(X: Tensor): Tensor

    Assign each sample to the nearest cluster center (exemplar).

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit 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>): this

    Set 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.

Example
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: Tensor

    Cluster labels of the training samples.

  • clusterCenters: Tensor

    Centroid (mean of the training samples) of every cluster, indexed by label.

  • nClusters: number

    Number of clusters found.

  • nLeaves: number

    Number of training samples (leaves of the dendrogram).

  • children: Tensor

    Children of every non-leaf node of the dendrogram.

  • distances: Tensor

    Distance 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): this

    Build the cluster hierarchy of X and cut it.

  • predict(X: Tensor): Tensor

    Predict cluster labels for new samples using nearest-neighbor assignment.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit 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>): this

    Set 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.

Example
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: Tensor

    Cluster labels of the samples passed to the last fit or partialFit call.

  • clusterCenters: Tensor

    Centers of the final clusters: the sample-count weighted mean of their leaf entries.

  • subclusterCenters: Tensor

    Centroids of the leaf entries of the CF tree.

  • subclusterLabels: Tensor

    Final cluster of every leaf entry.

  • fit(X: Tensor, _y?: Tensor): this

    Build the CF tree from X (discarding any previous tree) and cluster its leaf entries.

  • partialFit(X?: Tensor, _y?: Tensor): this

    Add the samples of X to the existing CF tree (or start one) and recompute the global clustering.

  • predict(X: Tensor): Tensor

    Assign each sample the label of its closest leaf entry.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit 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>): this

    Set 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

Example
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: Tensor

    Get cluster labels assigned during fitting.

  • nClusters: number

    Number of clusters found (excluding noise).

  • coreIndices: number[]

    Get indices of core samples discovered during fitting.

  • fit(X: Tensor, _y?: Tensor): this

    Perform DBSCAN clustering on data X.

  • predict(X: Tensor): Tensor

    Predict cluster labels for new samples using nearest-neighbor assignment.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit DBSCAN and return cluster labels.

  • getParams(): Record<string, unknown>

    Get hyperparameters for this estimator.

  • setParams(params: Record<string, unknown>): this

    Set 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.

Example
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: Tensor

    Component labels of the training samples.

  • clusterCenters: Tensor

    Component means.

  • means: Tensor

    Component means (same as clusterCenters).

  • weights: Tensor

    Mixing weights of the components; they sum to 1.

  • covariances: Tensor

    Covariances of the components.

  • converged: boolean

    Whether EM reached the tol threshold in the best run.

  • nIter: number

    Number of EM iterations of the best run.

  • lowerBound: number

    Mean log-likelihood per sample reached by the best run, evaluated before its last M-step.

  • fit(X: Tensor, _y?: Tensor): this

    Estimate the model parameters with the EM algorithm.

  • predict(X: Tensor): Tensor

    Predict the most probable component for each sample.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit the model and return the component labels of the training samples.

  • predictProba(X: Tensor): Tensor

    Posterior probability of each component for each sample.

  • scoreSamples(X: Tensor): Tensor

    Log-likelihood of each sample under the model.

  • score(X: Tensor): number

    Mean log-likelihood of the samples under the model.

  • bic(X: Tensor): number

    Bayesian information criterion of the model on X (lower is better).

  • aic(X: Tensor): number

    Akaike information criterion of the model on X (lower is better).

  • getParams(): Record<string, unknown>

    Get hyperparameters for this estimator.

  • setParams(params: Record<string, unknown>): this

    Set 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

Example
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: Tensor

    Get cluster centers.

  • labels: Tensor

    Get training labels.

  • inertia: number

    Get inertia (sum of squared distances of samples to their closest centroid).

  • nIter: number

    Get number of iterations run by the best run.

  • nFeaturesIn: number

    Number of features seen during fit.

  • fit(X: Tensor, _y?: Tensor): this

    Fit K-Means clustering on training data.

  • predict(X: Tensor): Tensor

    Predict the closest cluster for each sample.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit and predict in one step.

  • transform(X: Tensor): Tensor

    Transform X to cluster-distance space.

  • fitTransform(X: Tensor, _y?: Tensor): Tensor

    Fit the model and return the distances of the training samples to every centroid.

  • score(X: Tensor): number

    Opposite of the inertia of X under the fitted centroids (higher is better).

  • getParams(): Record<string, unknown>

    Get hyperparameters for this estimator.

  • setParams(params: Record<string, unknown>): this

    Set the parameters of this estimator.

  • clone(): KMeans

    Create 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.

Example
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: Tensor

    Training labels of shape (n_samples,); -1 marks samples left unclustered when clusterAll is false.

  • clusterCenters: Tensor

    Cluster centers of shape (n_clusters, n_features), strongest mode first.

  • nIter: number

    Largest number of shift iterations any seed needed.

  • fit(X: Tensor, _y?: Tensor): this

    Fit Mean Shift clustering.

  • predict(X: Tensor): Tensor

    Assign each sample to the nearest cluster center found during fit.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit 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.

Example
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: Tensor

    Cluster centers of shape (n_clusters, n_features).

  • labels: Tensor

    Labels of the training data (of the latest batch after partialFit).

  • inertia: number

    Sum of squared distances of the samples to their closest centroid.

  • nSteps: number

    Number of mini-batch steps performed by the selected run (plus any partialFit calls).

  • fit(X: Tensor, _y?: Tensor): this

    Fit Mini-Batch K-Means on training data.

  • partialFit(X: Tensor, _y?: Tensor): this

    Update the model with a single mini-batch (online learning).

  • predict(X: Tensor): Tensor

    Predict the closest cluster for each sample.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit the model and return the training labels.

  • transform(X: Tensor): Tensor

    Transform X to cluster-distance space.

  • fitTransform(X: Tensor, _y?: Tensor): Tensor

    Fit the model and return the distances of the training samples to every centroid.

  • score(X: Tensor): number

    Opposite of the inertia of X under 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.

Example
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: Tensor

    Training labels of shape (n_samples,); -1 marks noise.

  • reachability: Float64Array

    Reachability distance of every sample (indexed by sample, Infinity for unreachable ones).

  • ordering: Int32Array

    Sample indices in the order OPTICS visited them.

  • coreDistances: Float64Array

    Core distance of every sample; Infinity for samples that are not core points within maxEps.

  • predecessor: Int32Array

    For 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 in ordering, with nested clusters listed before the clusters that contain them.

  • clusterCenters: Tensor

    Mean of every cluster found, shape (n_clusters, n_features); noise is excluded.

  • fit(X: Tensor, _y?: Tensor): this

    Compute the OPTICS ordering and extract clusters.

  • predict(X: Tensor): Tensor

    Predict cluster labels for new samples using nearest-neighbor assignment.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit 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).

Example
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: Tensor

    Training labels of shape (n_samples,).

  • clusterCenters: Tensor

    Mean of the training samples of every non-empty cluster in label order, shape (n_clusters, n_features).

  • fit(X: Tensor, _y?: Tensor): this

    Fit Spectral Clustering.

  • predict(X: Tensor): Tensor

    Predict cluster labels for new samples using nearest-neighbor assignment.

  • fitPredict(X: Tensor, _y?: Tensor): Tensor

    Fit the model and return the training labels.

  • getParams(): Record<string, unknown>
  • setParams(params: Record<string, unknown>): this
ml-clustering.ts
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());