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.1.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.1-cp311-abi3-win_amd64.whl (1.7 MB view details)

Uploaded CPython 3.11+Windows x86-64

betula_cluster-0.1.1-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.1-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.1-cp311-abi3-macosx_11_0_arm64.whl (1.4 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

betula_cluster-0.1.1-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.1.tar.gz.

File metadata

  • Download URL: betula_cluster-0.1.1.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.1.tar.gz
Algorithm Hash digest
SHA256 df72361fc94115e23f4c9dce5d0e277229d20a5d0fe5df238350a78c7ff4b614
MD5 00f6f8594e20611ac445d2802704dd6b
BLAKE2b-256 d1aaef5c5c30572b51ad3f641a1f39ac68787b75107426131718361353002c84

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.1.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.1-cp311-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.1-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 dd99934daa16e502e9398f5dedd9885108c08d7cbaf1f2eb9c8ff1b4a91018bc
MD5 4e8db797825e213b7c80f9fea799abb0
BLAKE2b-256 d5be5bb1b00daba22fc29c73ef5e2bffa9ba522e2a56ac71a67b0efed7d4c838

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.1-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.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 738fe444dcb3968d21d07e08f499047aa456ddb9134cdfa606141b29f04a339d
MD5 867467db167846c265a4df83747fb9f3
BLAKE2b-256 d8d8f517587bf99aa04ed3a8ca52fa31f265340ea887805102cfee8194faf076

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.1-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.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 a25394546ed1e6db112bf2975133157cddc04a72e2da04ae32ebd8bd87e8819b
MD5 5041bd0dcd9b743c876297614498256b
BLAKE2b-256 15d33b59b03f009bcdb31643ae0bb2e00d2a8e77d4e9b73080cab8bff484874c

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.1-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.1-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.1-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 56a504d4fb3ef98bb99df39a7abd6245b415a9227665fde14b4e977eecbe6a42
MD5 7a272f9a34a47d6c4bf81e614a2c3e6b
BLAKE2b-256 58ab89d249ace18a56b082f6bf7aa33c55763ba3da07e2f7d569bebbb6f9748b

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.1-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.1-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for betula_cluster-0.1.1-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 82ae177a39b01c5c660b8bcdb8bfb5d9d650e3ad1634a575c4f779da4b7943a8
MD5 f075ff4ee0e106110d85c9290737bdda
BLAKE2b-256 a89abdc0c642dae5666d32682432d6838f063b449c5c9ca7a2a42d3d04f21a0a

See more details on using hashes here.

Provenance

The following attestation bundles were made for betula_cluster-0.1.1-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