Skip to main content

Fast, numerically stable BETULA clustering with a Rust core

Project description

betula-cluster

PyPI Python CI Python coverage 100% License: MIT Rust core · PyO3

Fast, memory-bounded clustering for large tabular & embedding data. A numerically stable BETULA CF-tree with a full set of clustering heads — k-means · GMM (diagonal & full) · Ward · HDBSCAN · Mapper — plus streaming partial_fit and a scikit-learn API. From-scratch Rust core, PyO3 bindings, no LAPACK, no SciPy at runtime.

pip install betula-cluster

At a glance — honest benchmarks

Measured against scikit-learn on StandardScaler-normalized data, each method in its own subprocess with peak RSS sampled from /proc/self/statm. Full methodology, every metric, and all tables (wins and losses) live in bench/RESULTS.md.

  • 🎯 Quality at parity. betula's k-means / GMM / Ward land within ≈0.01 ARI of their scikit-learn counterparts; full-covariance GMM recovers anisotropic clusters just as well (0.90 vs 0.90); HDBSCAN-on-CF nails non-convex moons & circles (ARI 1.00).
  • 15–40× faster at N = 1 000 000. betula-kmeans labels a million points in 0.20 s vs scikit-learn KMeans 3.3 s (17×), Birch 8.0 s (40×), GaussianMixture 5.5 s (27×).
  • 🪶 Bounded memory. Streaming 10 M points peaks at ~57 MB — flat in N — while an in-core KMeans must hold the array and peaks at ~5 GB (≈88× less, and the gap grows without limit).
  • 🌍 Real data, not just blobs. Matches scikit-learn on digits (k-means 0.53 vs 0.47) and clusters full covtype (581 k rows) ~7× faster at better ARI; in very high dimensions (MNIST, 784-D) normalize=True beats scikit-learn (k-means 0.44 vs 0.32) — full table, and the honest trade-offs, in bench/RESULTS.md.
Fit time vs N Peak memory vs N
Phase-3 clusters only the ~2 000 leaf microclusters, not the raw points, so every head finishes 1 M points in under ⅓ s. The CF-tree is capped by max_leaves, so streaming memory stays flat — it clusters data larger than RAM.

Why

Clustering libraries tend to either not scale (full GMM/HDBSCAN on raw points), lose precision (classic BIRCH computes variance as SS − ‖LS‖²/n, which catastrophically cancels far from the origin), or blow up in memory (BIRCH-family subcluster explosion in high dimensions). betula-cluster addresses all three:

  • Numerically stable — clustering features (n, μ, S) via Welford / Chan updates; the covariance is PSD by construction. Classic BIRCH loses all digits near coordinate 1e7; betula does not.
  • Memory-bounded by design — the CF-tree caps its leaves (max_leaves) and rebuilds, so it never explodes; streaming memory is flat in N and clusters data larger than RAM.
  • Complete — one stable engine spanning k-means / GMM (diag & full) / Ward / HDBSCAN-style / Mapper, with streaming partial_fit, a scikit-learn API, and dataset-structure inspection.

The math (stable CF, the expected-log GMM E-step, distance derivations, relation to BIRCH/BETULA) is written up — verified symbolically and numerically — in docs/MATH.md.

Quick start

import numpy as np
import betula_cluster

X = np.random.default_rng(0).normal(size=(100_000, 10))

labels = betula_cluster.fit_predict(X, n_clusters=10, method="kmeans")
labels = betula_cluster.fit_predict(X, n_clusters=0, feature="full", method="gmm-full")  # auto-k via BIC
labels = betula_cluster.fit_predict(X, method="hdbscan", min_cluster_size=25)             # -1 == noise

Streaming / out-of-core — feed chunks, finalize, predict; memory stays bounded by max_leaves:

est = betula_cluster.Betula(method="gmm", memory_budget_mb=512)
for chunk in stream_of_arrays:        # each chunk is a 2-D float32/float64 array
    est.partial_fit(chunk)
est.partial_fit()                     # finalize the global clustering over everything seen
labels = est.predict(X_query)

Constraints (COP-KMeans), mixed numeric+categorical (KPrototypes), streaming density (DenStream / DbStream), quantile sketches, scipy.sparse input, soft assignment / coresets / diagnostics, the Rust API, and the CLI — all in the usage guide.

Capabilities

  • Clustering heads — weighted k-means, GMM (diagonal & full covariance, BIC auto-k), exact Ward HAC, HDBSCAN-style density over CF microclusters, and a Mapper topological skeleton.
  • Streamingpartial_fit at bounded memory; DenStream & DbStream for evolving streams; mergeable KllSketch / DdSketch quantiles.
  • Data types — dense f32/f64, scipy.sparse (never densified), O(nnz) sparse-native, and mixed numeric+categorical (k-prototypes).
  • Beyond labelspredict_proba, coresets, diagnostics, outliers / near-duplicates / representatives, drift snapshots, COP-KMeans constraints, and robust (Huber) insertion.
  • Engineering — scikit-learn API (Pipeline / clone / GridSearchCV), typed abi3 wheel, save / load + pickle, a dependency-free CLI, and a reusable Rust core.

Full reference: docs/FEATURES.md.

Examples

Twelve executed, plotted notebooks — one per capability — live in examples/ (render on GitHub):

And three end-to-end use cases (each scored against ground truth):

Documentation

  • Usage guide — runnable snippets for every interface.
  • Features — full capability reference + crate architecture.
  • Math — stable CF, GMM E-step, distance derivations, relation to BIRCH/BETULA.
  • Benchmarks — methodology, every metric, all tables, honest wins & losses.
  • Design — internal design, invariants, and testing strategy.

Verified: 129 Rust unit/integration tests + a 123-case Python suite at 100% wrapper coverage (Rust ≥95%, CI-enforced), clippy -D warnings + fmt clean across all feature sets, on Python 3.11–3.14 (single abi3 wheel).

Known limitations

Honest scope — inherent to a CF-compression + streaming design, not bugs:

  1. Insertion-order sensitive — like every BIRCH-family streaming method, the labels depend on the order points arrive (the parallel build differs from the serial one, as a different order would).
  2. threshold / max_leaves are real hyperparameters — they trade compression against resolution; n_rebuilds_ / threshold_ expose thrashing / over-coarsening.
  3. CF-level heads approximate raw-data clustering — Phase-3 runs on the M ≪ N microclusters; quality degrades when clusters overlap at the compression scale. Mitigation: more leaves.
  4. HDBSCAN-on-CF ≠ raw-point HDBSCAN — mass-aware HDBSCAN over microclusters: fast and close, but an approximation (weaker on overlapping blobs; see the benchmarks).
  5. The expected-log GMM optimizes a CF-level objective, not pointwise EM (a deliberate, measured choice for coarse CFs).
  6. Frequent-Directions is an approximate low-rank covariance (exact only up to its rank ).

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

betula_cluster-0.1.0.tar.gz (4.3 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

betula_cluster-0.1.0-cp311-abi3-win_amd64.whl (1.7 MB view details)

Uploaded CPython 3.11+Windows x86-64

betula_cluster-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

betula_cluster-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.4 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

betula_cluster-0.1.0-cp311-abi3-macosx_11_0_arm64.whl (1.4 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

betula_cluster-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file betula_cluster-0.1.0.tar.gz.

File metadata

  • Download URL: betula_cluster-0.1.0.tar.gz
  • Upload date:
  • Size: 4.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for betula_cluster-0.1.0.tar.gz
Algorithm Hash digest
SHA256 51856be0c2ecdab2cf403d929c885ecc7b40185d4514b7fa0543d059fbb32acb
MD5 df0000738c8db8a8dda829082130836d
BLAKE2b-256 4c0f584f635d8a7e262c8b7024d89356072a22bd26189feb5b8e966305a9a4a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.0.tar.gz:

Publisher: release.yml on ilgrad/betula-cluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file betula_cluster-0.1.0-cp311-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 095c5e97f8a6f757a4d7c8ed3281e1bf53ff596fe0242e07ca7dfca801993826
MD5 cc1891299b125e1d3e904351ff4e9cdf
BLAKE2b-256 f6b6128c0e9fe501b58116a7034e07ceac54785cb2fa3db0cbdebb207bf7cd6b

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.0-cp311-abi3-win_amd64.whl:

Publisher: release.yml on ilgrad/betula-cluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file betula_cluster-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0dd649a268ff2a698d24dab97609af8e12d1df1476aee4e188db7bdbf359b456
MD5 71edbfe54f3bac0ef14e31e88741cc67
BLAKE2b-256 95ea4e8d3235172be776cdf705bb466f1f16847f5f896b67ff034428e20964ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on ilgrad/betula-cluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file betula_cluster-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 99692db071b68fd4292625cdfc96afb6af863e0bebcafcab031116db0204c56a
MD5 251f2f77ac17465c35a1f16c764a57c8
BLAKE2b-256 b9695ca8f7d7a621109e7e2007429874ea5541b6b13164482ffaf0debaa25163

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on ilgrad/betula-cluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file betula_cluster-0.1.0-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.0-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 07992e68aa34f8dd68086a201e827fbee11ea0b4e2ef6085b652f4035adbf4e5
MD5 e7d879a8e06d63264fddc90aa55c3567
BLAKE2b-256 f26dc9d974ea72153e985519f717f9c453d824a8980276202984d6cd7a223762

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.0-cp311-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on ilgrad/betula-cluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file betula_cluster-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 e442a989171260306293c162dd75226c9273b22d77dfce9968ca56b80d7e6bf3
MD5 b685ebaf06a6fdebf584ff7f90be7e17
BLAKE2b-256 2b74825b02f35121fa23bad15fc333a28272f627f1d3a1a06e6449c5ab3fcae3

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on ilgrad/betula-cluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page