count_split
count_split reproducibly allocates observed count molecules into statistically
independent analysis folds. It is a Python implementation inspired by Neufeld
et al.'s count-splitting approach for avoiding double use of the same counts in
single-cell clustering and differential-expression analysis.
- Paper: Inference after latent variable estimation for single-cell RNA sequencing data
- Reference R package and methodology: countsplit
Please cite the original methodology when using this package.
Allocation contract
For every observed molecule, the implementation draws one uniform random value
and assigns that molecule to exactly one requested fold. Multi-fold allocation
is simultaneous: [0.325, 0.325, 0.35] means those final probabilities, not a
sequence of percentages applied to a shrinking remainder.
The implementation guarantees:
- element-wise count conservation across all output folds;
- deterministic results for the same seed and canonically ordered matrix;
- identical seeded allocation for equivalent dense, CSR, and CSC inputs;
- no sparse-to-dense conversion;
- preservation of integer input dtypes on output (CSC for every sparse input format); and
- explicit rejection of negative, non-finite, fractional, boolean, complex, or out-of-range counts.
Installation
From a checkout:
python -m pip install .
For development:
python -m pip install -e .
python -m pytest
Python 3.10 or newer is required.
Usage
The legacy API expects variables in rows and samples in columns. For a single-cell matrix, that means genes × cells.
import numpy as np
from count_split.count_split import multi_split
counts = np.random.default_rng(7).negative_binomial(
2,
0.4,
size=(1_000, 500),
)
train, validation, test = multi_split(
counts,
percent_vect=[0.325, 0.325, 0.35],
seed=123456,
)
assert np.array_equal(train + validation + test, counts)
Weights are normalized, so [1, 1, 2] is equivalent to
[0.25, 0.25, 0.5]. A zero-weight fold is retained as an all-zero result.
multi_split continues to return a list for backward compatibility.
Sparse matrices
CSR, CSC, and other SciPy sparse inputs are accepted. Duplicate sparse
coordinates are summed once in safe int64 scratch space. All sparse outputs
are canonical CSC matrices and preserve the input integer dtype when it can
represent the canonicalized logical counts.
from scipy import sparse
from count_split.count_split import multi_split
counts_csr = sparse.csr_matrix(counts)
train, validation, test = multi_split(
counts_csr,
percent_vect=[0.325, 0.325, 0.35],
seed=123456,
)
assert ((train + validation + test) != counts_csr).nnz == 0
AnnData
AnnData normally stores cells in rows and genes in columns, so transpose X
for this legacy API:
train, validation, test = multi_split(
adata.X.T,
percent_vect=[0.325, 0.325, 0.35],
seed=123456,
)
The returned matrices are genes × cells. Downstream code that expects the AnnData orientation should transpose each result back.
Two-fold APIs
The original pairwise functions and argument order remain available. The new
seed parameter is appended, so existing positional calls remain valid.
from count_split.count_split import split_mat_counts, split_sparse
dense_a, dense_b = split_mat_counts(counts, percent_1=0.4, seed=17)
sparse_a, sparse_b = split_sparse(counts_csr, percent_1=0.4, seed=17)
Calling seed_rng(value) before a call that omits seed remains supported for
legacy integrations. New code should use the explicit seed argument. Valid
explicit seeds are integers from 0 through 4,294,967,295.
Dense HDF5
split_mat_counts_h5 preserves the original two-output file interface and
copies all non-target HDF5 content into both outputs. Only the selected dense,
two-dimensional dataset is replaced with split counts.
from count_split.count_split import split_mat_counts_h5
split_mat_counts_h5(
"input.h5",
"fold_a.h5",
"fold_b.h5",
percent_1=0.5,
bin_size=5_000,
key="infile",
seed=17,
)
Memory and reproducibility notes
All cumulative offsets and total-molecule bookkeeping use int64 internally,
but this does not force int64 storage on output. Each output entry is bounded
by its corresponding canonicalized input count. Integral real-valued inputs are
accepted for legacy compatibility but return integer (int64) folds.
The chosen non-parametric algorithm explicitly materializes one uniform value and one fold index per observed molecule, plus a folds × nonzero-entry re-collation buffer. Its peak memory therefore scales with the total molecule count, not only matrix dimensions or sparse nonzeros. This is deliberate and should be capacity-planned for very deep matrices.
For ordinary dense and sparse calls, bin_size is accepted only for signature
compatibility and does not change matrix-wide allocation. The HDF5 function
uses bin_size to process contiguous column chunks while maintaining one RNG
stream; seeded output is invariant to that chunk size.
Reproducibility requires the same package/runtime stack, input values, matrix shape, row/column order, fold weights, and seed. Equivalent dense, CSR, and CSC representations are canonicalized to the same column-major allocation order.
Compatibility
Version 1.0.0 retains the original public function names, existing positional
arguments, HDF5 behavior, CSC sparse return convention, and list return from
multi_split. Correctness-related behavior changes are documented in
CHANGELOG.md.
License and contact
Licensed under the GNU Affero General Public License v3.0; see LICENSE.
Source: github.com/scottyler89/count_split
Repository owner: scottyler89@gmail.com
Metadata
Release files for count-split 1.0.1
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 | |
|---|---|---|---|---|
| count_split-1.0.1.tar.gz | 27.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| count_split-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.5 kB
Release files / count_split-1.0.1.tar.gz
| Download URL | count_split-1.0.1.tar.gz |
|---|---|
| Size | 27.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f380c754e29fd55df86bad1ce0e21ad272b28b6ef0844c9ff5bd343abcebb875
|
|
BLAKE2b-256 checksum How to use checksums |
c7af865927bbff8269fd02fd2349d81e4186567047c5d0d7b0c0e00f8df78701
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logRelease files / count_split-1.0.1-py3-none-any.whl
| Download URL | count_split-1.0.1-py3-none-any.whl |
|---|---|
| Size | 20.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b7e1a323d9a4d298368b026dbb6dc551a741b0d60672e715c5b43044b8b84acf
|
|
BLAKE2b-256 checksum How to use checksums |
edf737538be518c2662be7a51772751cc3189f25fea0c81ddae4fbeff91e191d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logSecurity for count-split 1.0.1
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for count_split-1.0.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logA 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.