mojolearn.AgglomerativeClustering#

class mojolearn.AgglomerativeClustering(n_clusters=2, *, metric='euclidean', connectivity='pairwise', linkage='single', c=15, memory=None, compute_full_tree='auto', distance_threshold=None, compute_distances=False, prediction_data=False)[source]#

Bases: object

Agglomerative hierarchical clustering.

Builds a merge tree bottom-up and cuts it into n_clusters clusters or at distance_threshold. Supports scikit-learn’s single, complete, average and Ward linkages, connectivity matrices and per-merge distances. Input is a dense array, converted to float32.

Two defaults differ from scikit-learn’s and change the result: linkage is ‘single’ (scikit-learn: ‘ward’) and connectivity is ‘pairwise’, the full dense graph (scikit-learn’s None). cuML’s AgglomerativeClustering also defaults to single linkage, but to ‘knn’ connectivity, which is not available here; pass connectivity='pairwise' explicitly when moving cuML code.

Label integers can differ from scikit-learn’s for the same partition: compare partitions (for example with adjusted_rand_score), not the label values. Likewise, children_ rows can differ in order and orientation from scikit-learn’s when merge distances tie.

Parameters:
  • n_clusters (int or None, default 2) – Number of clusters, between 1 and the number of samples. Must be None when distance_threshold is set.

  • metric (str, default ‘euclidean’) – One of ‘euclidean’, ‘l2’, ‘l1’, ‘manhattan’, ‘cityblock’, ‘cosine’ or ‘precomputed’ (X is then a square distance matrix). Any other value, including a callable, raises NotImplementedError. Ward linkage requires ‘euclidean’ or ‘l2’; ‘cosine’ rejects rows that are all zeros.

  • connectivity (‘pairwise’, None, array-like, sparse matrix or callable, default ‘pairwise’) – ‘pairwise’ and None both mean no constraint (the full dense graph). A matrix of shape (n_samples, n_samples), or a callable that returns one from X, restricts merges to its nonzero entries, as in scikit-learn. ‘knn’ raises NotImplementedError.

  • linkage ({‘single’, ‘complete’, ‘average’, ‘ward’}, default ‘single’) – Merge criterion.

  • c (int, default 15) – Kept for cuML compatibility, where it tunes the ‘knn’ graph. Any other value raises NotImplementedError.

  • memory (None) – Must be None; anything else raises NotImplementedError.

  • compute_full_tree (‘auto’ or bool, default ‘auto’) – With a connectivity matrix, whether to build the full tree or stop at n_clusters, with scikit-learn’s ‘auto’ rule. Without one the full tree is always built. Must be True or ‘auto’ when distance_threshold is set.

  • distance_threshold (float or None, default None) – Merge distance at or above which clusters are no longer merged. Exactly one of n_clusters and distance_threshold must be set.

  • compute_distances (bool, default False) – Store the merge distances in distances_ even without distance_threshold.

  • prediction_data (bool, default False) – Keep a copy of the training rows so that predict and save work. Only single linkage with the ‘euclidean’ or ‘l2’ metric can predict.

Variables:
  • labels_ (Array of shape (n_samples,), int32) – Cluster of each training row.

  • n_clusters_ (int) – Number of clusters found.

  • children_ (Array of shape (n_merges, 2), int32) – The merges, one per row; values below n_samples are rows, larger values are earlier merges. n_merges is n_samples - 1 for a full tree.

  • distances_ (Array of shape (n_merges,), float32) – Merge distances. Set only with distance_threshold or compute_distances=True.

  • n_leaves_ (int) – Number of leaves (n_samples).

  • n_connected_components_ (int) – Connected components of the connectivity graph. A constrained fit with more than one warns and completes the graph, as scikit-learn does.

  • n_boruvka_rounds_ (int) – Not a scikit-learn attribute: the number of Boruvka rounds of the single-linkage euclidean fit without a connectivity matrix; -1 on other fits and on a CPU-only install.

  • n_features_in_ (int) – Number of features seen in fit.

Examples

>>> import numpy as np, mojolearn
>>> X = np.random.default_rng(0).random((1000, 8), dtype=np.float32)
>>> model = mojolearn.AgglomerativeClustering(n_clusters=4, prediction_data=True).fit(X)
>>> labels = model.predict(X[:10])
__init__(n_clusters=2, *, metric='euclidean', connectivity='pairwise', linkage='single', c=15, memory=None, compute_full_tree='auto', distance_threshold=None, compute_distances=False, prediction_data=False)[source]#

Methods

fit(X[, y])

Build the merge tree of X and cut it into clusters.

fit_predict(X[, y])

Fit the model on X and return the training labels.

load(path)

Load a model written by save.

predict(X)

Label new rows with the cluster of their nearest training row.

save(path)

Save the model's prediction data to path.

fit(X, y=None)[source]#

Build the merge tree of X and cut it into clusters.

Parameters:
  • X (array-like of shape (n_samples, n_features)) – Dense training rows, or a square distance matrix with metric='precomputed'. Converted to float32. Sparse input raises NotImplementedError.

  • y (ignored) – Present for scikit-learn compatibility.

Returns:

self (AgglomerativeClustering) – The fitted estimator.

Raises:
  • TypeError – If prediction_data is not a bool.

  • NotImplementedError – If X is sparse.

  • ValueError – If there are fewer than 2 rows, n_clusters exceeds the number of rows, X contains NaN or infinity, a precomputed or connectivity matrix has the wrong shape, or metric='cosine' meets an all-zero row. With single linkage, the euclidean metric, no connectivity matrix and no merge distances requested, more than 46,340 rows also raises (the dense graph would overflow), except in fast mode on Apple with at most 64 features.

Warns:

UserWarning – If a connectivity matrix has more than one connected component.

fit_predict(X, y=None)[source]#

Fit the model on X and return the training labels.

Parameters:
  • X (array-like of shape (n_samples, n_features)) – Dense training rows, or a square distance matrix with metric='precomputed'.

  • y (ignored) – Present for scikit-learn compatibility.

Returns:

labels (Array of shape (n_samples,), int32) – The fitted labels_.

predict(X)[source]#

Label new rows with the cluster of their nearest training row.

Neither scikit-learn nor cuML has this method. For single linkage, the closest cluster to a new row is the one containing its nearest training row (squared euclidean distance); ties go to the lowest cluster label, then the lowest training index. A training row predicts its own fitted label unless an exact duplicate of it was cut into a cluster with a lower label, which happens only when n_clusters exceeds the number of distinct rows.

Only single linkage with the ‘euclidean’ or ‘l2’ metric can predict, and only after a fit with prediction_data=True (or a model loaded with load).

Parameters:

X (array-like of shape (n_queries, n_features)) – Rows to label, with the number of features seen in fit.

Returns:

labels (Array of shape (n_queries,), int32) – Cluster of each row, numbered as labels_.

Raises:
  • ValueError – If the model is not fitted or was fitted without prediction_data=True.

  • NotImplementedError – If the linkage or metric has no prediction rule.

save(path)[source]#

Save the model’s prediction data to path.

Writes an npz file with the training rows, labels_, n_clusters_, n_features_in_, n_leaves_, the metric and the numeric mode. children_ and n_boruvka_rounds_ are not saved, so the same fit writes the same bytes on every device; a loaded model has neither attribute. A loaded model predicts, including on a CPU-only install.

Parameters:

path (str or path-like) – File to write.

Raises:
classmethod load(path)[source]#

Load a model written by save.

The loaded model can predict. Every array is read at its saved dtype and never cast.

Parameters:

path (str or path-like) – File written by save.

Returns:

model (AgglomerativeClustering) – The loaded model.

Raises:

ValueError – If the file is not a saved AgglomerativeClustering or its arrays are inconsistent.