This release is a pre-release and may not be stable for production use.
nanoom
Multimodal-multitask-balanced dataset splitting: cluster-based separation and task-stratification/balancing across splits.
Design rationale is in ARCHITECTURE.md, the task list in DEVELOPMENT.md, setup and conventions in CONTRIBUTING.md.
Install
pip install nanoom
Core dependencies: polars, pulp, and scikit-learn.
Optional extras:
nanoom[chem]- rdkit/bblean-based clustering methods for molecular datananoom[tutorial]- marimo, for the notebooks inexamples/nanoom[plotting]- reserved for future split-quality visualization (not yet implemented)
Splitting
nanoom.split takes a dataframe with a pre-computed cluster column (see
Clustering below) and returns a leakage-safe, task-balanced
train/val/test assignment.
import nanoom
# balances by exactly solving a linear program (Tricarico et al., ported from
# https://github.com/sohviluukkonen/gbmt-splits); task columns may be a mix of
# regression, classification, and string columns, balanced jointly
out = nanoom.split(
df,
y_cols=["pchembl_value_mean", "target_id"],
cluster_col="cluster",
n_splits=3,
method="tricarico",
)
# or: a lighter/faster fallback via sklearn's StratifiedGroupKFold
out = nanoom.split(
df,
y_cols="ic50",
cluster_col="cluster",
n_splits=3,
method="sklearn",
)
Both methods return df with a split column added, holding 0..n_splits-1 per
row (rename it with split_col=). The cluster column stays in the frame, so the
per-cluster assignment is
out.group_by("cluster").agg(pl.col("split").first()).
Splitting without clusters
cluster_col is optional. Passing cluster_col=None splits rows directly with no
leakage constraint — each row becomes its own cluster, so the LP balances rows and
sklearn falls back to StratifiedKFold:
out = nanoom.split(df, y_cols="ic50", n_splits=3, method="tricarico")
A cluster column of row indices is added so grouping and the audit functions
below work the same in both modes. Note that the LP carries one binary variable per
cluster per split, so cluster-free mode on a large frame gets slow; nanoom warns
past 1000 clusters and suggests relative_gap for a fast near-optimal solve.
For regression task columns, method="tricarico" bins values before balancing
(binning_approach=, default "qcut"): "qcut" targets roughly equal n_datapoints per
bin; "gbmt_splits" instead bins the distinct values (matching gbmt-splits'
behaviour).
for repeated values this can skew row counts across bins. Neither option fully
resolves a very-repeated value, since those rows can't be split
across a bin boundary either way.
Clustering
nanoom.cluster produces the cluster_col that split() expects, decoupled from
splitting itself so you can bring your own cluster assignment instead if you prefer.
clusters = nanoom.cluster(descriptors, method="kmeans", n_clusters=20)
Supports kmeans, dbscan, hdbscan, random, and hash_dummy out of the box;
sphere_exclusion, bitbirch, maxmin, and leader_picker additionally require
nanoom[chem]. An auto mode that adaptively tunes cluster granularity to trade off
leakage-avoidance against task-balance quality is planned for a later iteration.
Auditing a split
nanoom.eval answers "is my split actually leakage-free and balanced?":
from nanoom.eval import check_no_group_overlap, check_distribution_y_similar, min_distances_splits
check_no_group_overlap(out["cluster"], out["split"]) # raises if any cluster spans >1 split
check_distribution_y_similar(out["ic50"], out["split"]) # raises if a split's y-mean drifts >10%
min_distances_splits(descriptors, out["split"], metric="euclidean") # inter/intra-split distance stats
All three take per-row arrays and raise on a length mismatch rather than silently scoring the wrong subset.
Metadata
Release files for nanoom 0.1.0a20261002
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| nanoom-0.1.0a20261002.tar.gz | 31.6 kB | Without provenance | Details for nanoom-0.1.0a20261002.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| nanoom-0.1.0a20261002-py3-none-any.whl | Python 3 | none | any | Details for nanoom-0.1.0a20261002-py3-none-any.whl |
Total release size: 65.1 kB
Release files / nanoom-0.1.0a20261002.tar.gz
| Download URL | nanoom-0.1.0a20261002.tar.gz |
|---|---|
| Size | 31.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b35d61ce8f2f4ad701fe27e7e33626f7a8a1dd698a86d9af44351925ebecfb1f
|
|
BLAKE2b-256 checksum How to use checksums |
d5996d9d8f55fb0cf0383c707f5eab32ae59ccfe0ff2dc71556d0b2ceed21e90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / nanoom-0.1.0a20261002-py3-none-any.whl
| Download URL | nanoom-0.1.0a20261002-py3-none-any.whl |
|---|---|
| Size | 33.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
87f82433794baef94aa286f1a5e2c1772cb67a43347f5607b7a757efa957e8c0
|
|
BLAKE2b-256 checksum How to use checksums |
bcf6a2eca3b0c11802f0e00d93dd35e98c4389fa66df6d8060387cacf0ad2a7a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for nanoom 0.1.0a20261002
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.