annslicer 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- annslicer-0.1.3/.github/workflows/benchmark.yml +26 -0
- annslicer-0.1.3/.github/workflows/build.yml +31 -0
- annslicer-0.1.3/.github/workflows/lint.yml +30 -0
- annslicer-0.1.3/.github/workflows/publish.yml +51 -0
- annslicer-0.1.3/.github/workflows/test.yml +33 -0
- annslicer-0.1.3/.gitignore +40 -0
- annslicer-0.1.3/CONTRIBUTING.md +132 -0
- annslicer-0.1.3/LICENSE.md +26 -0
- annslicer-0.1.3/Makefile +23 -0
- annslicer-0.1.3/PKG-INFO +187 -0
- annslicer-0.1.3/README.md +151 -0
- annslicer-0.1.3/benchmarks/bench_slice.py +303 -0
- annslicer-0.1.3/benchmarks/conftest.py +104 -0
- annslicer-0.1.3/diagram.png +0 -0
- annslicer-0.1.3/pyproject.toml +109 -0
- annslicer-0.1.3/setup.cfg +4 -0
- annslicer-0.1.3/src/annslicer/__init__.py +16 -0
- annslicer-0.1.3/src/annslicer/_store.py +61 -0
- annslicer-0.1.3/src/annslicer/_version.py +34 -0
- annslicer-0.1.3/src/annslicer/cli.py +47 -0
- annslicer-0.1.3/src/annslicer/merge.py +299 -0
- annslicer-0.1.3/src/annslicer/slice.py +215 -0
- annslicer-0.1.3/src/annslicer.egg-info/PKG-INFO +187 -0
- annslicer-0.1.3/src/annslicer.egg-info/SOURCES.txt +29 -0
- annslicer-0.1.3/src/annslicer.egg-info/dependency_links.txt +1 -0
- annslicer-0.1.3/src/annslicer.egg-info/entry_points.txt +2 -0
- annslicer-0.1.3/src/annslicer.egg-info/requires.txt +16 -0
- annslicer-0.1.3/src/annslicer.egg-info/top_level.txt +1 -0
- annslicer-0.1.3/tests/conftest.py +100 -0
- annslicer-0.1.3/tests/test_merge.py +220 -0
- annslicer-0.1.3/tests/test_slice.py +206 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Benchmark
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
benchmark:
|
|
8
|
+
name: Benchmark (Python 3.10)
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
# Fetch full history so setuptools-scm can derive a version from tags.
|
|
15
|
+
fetch-depth: 0
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.10"
|
|
20
|
+
cache: pip
|
|
21
|
+
|
|
22
|
+
- name: Install package with dev dependencies
|
|
23
|
+
run: pip install -e ".[dev]"
|
|
24
|
+
|
|
25
|
+
- name: Run benchmarks
|
|
26
|
+
run: make benchmark
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Build check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
name: Build sdist and wheel
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0 # setuptools-scm needs full history + tags
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.10"
|
|
22
|
+
cache: pip
|
|
23
|
+
|
|
24
|
+
- name: Install build tools
|
|
25
|
+
run: pip install --upgrade pip build twine
|
|
26
|
+
|
|
27
|
+
- name: Build sdist and wheel
|
|
28
|
+
run: python -m build
|
|
29
|
+
|
|
30
|
+
- name: Check distributions
|
|
31
|
+
run: twine check dist/*
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
name: ruff
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
|
|
20
|
+
- name: Install ruff and mypy
|
|
21
|
+
run: pip install ruff mypy==1.15.0
|
|
22
|
+
|
|
23
|
+
- name: Check (lint)
|
|
24
|
+
run: ruff check src/ tests/
|
|
25
|
+
|
|
26
|
+
- name: Check (format)
|
|
27
|
+
run: ruff format --check src/ tests/
|
|
28
|
+
|
|
29
|
+
- name: Type-check (mypy)
|
|
30
|
+
run: mypy src/annslicer
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Runs automatically when a GitHub Release is published, AND can be
|
|
4
|
+
# triggered manually from the Actions tab as a fallback.
|
|
5
|
+
#
|
|
6
|
+
# Before creating a release:
|
|
7
|
+
# git tag v0.x.y
|
|
8
|
+
# git push --tags
|
|
9
|
+
# git push origin --tags
|
|
10
|
+
# Then: GitHub → Releases → Draft a new release → choose the tag → Publish.
|
|
11
|
+
#
|
|
12
|
+
# PyPI Trusted Publishing (OIDC) is used — no API token stored in secrets.
|
|
13
|
+
# One-time setup on PyPI: add a "Trusted Publisher" for this repo/workflow.
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
release:
|
|
17
|
+
types: [published]
|
|
18
|
+
workflow_dispatch:
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build-and-publish:
|
|
22
|
+
name: Build and publish to PyPI
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
environment: pypi # must match the environment name set in PyPI trusted publisher config
|
|
25
|
+
|
|
26
|
+
# Required for PyPI Trusted Publishing (OIDC token exchange)
|
|
27
|
+
permissions:
|
|
28
|
+
id-token: write
|
|
29
|
+
contents: read
|
|
30
|
+
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 0 # Full history so setuptools-scm picks up the tag
|
|
35
|
+
|
|
36
|
+
- uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.10"
|
|
39
|
+
|
|
40
|
+
- name: Install build tools
|
|
41
|
+
run: pip install --upgrade pip build twine
|
|
42
|
+
|
|
43
|
+
- name: Build sdist and wheel
|
|
44
|
+
run: python -m build
|
|
45
|
+
|
|
46
|
+
- name: Check distributions
|
|
47
|
+
run: twine check dist/*
|
|
48
|
+
|
|
49
|
+
- name: Publish to PyPI
|
|
50
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
51
|
+
# No username/password needed — OIDC handles authentication.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: pytest (Python ${{ matrix.python-version }})
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
# Fetch full history so setuptools-scm can derive a version from tags.
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: ${{ matrix.python-version }}
|
|
27
|
+
cache: pip
|
|
28
|
+
|
|
29
|
+
- name: Install package with dev dependencies
|
|
30
|
+
run: pip install -e ".[dev]"
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: pytest
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.so
|
|
7
|
+
|
|
8
|
+
# Distribution / packaging
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
*.egg
|
|
13
|
+
.eggs/
|
|
14
|
+
MANIFEST
|
|
15
|
+
|
|
16
|
+
# setuptools-scm generated version file
|
|
17
|
+
src/annslicer/_version.py
|
|
18
|
+
|
|
19
|
+
# Virtual environments
|
|
20
|
+
.venv/
|
|
21
|
+
venv/
|
|
22
|
+
env/
|
|
23
|
+
|
|
24
|
+
# Pytest
|
|
25
|
+
.pytest_cache/
|
|
26
|
+
.cache/
|
|
27
|
+
|
|
28
|
+
# Ruff
|
|
29
|
+
.ruff_cache/
|
|
30
|
+
|
|
31
|
+
# Jupyter
|
|
32
|
+
.ipynb_checkpoints/
|
|
33
|
+
|
|
34
|
+
# macOS
|
|
35
|
+
.DS_Store
|
|
36
|
+
|
|
37
|
+
# Large data files — never commit .h5ad files to the repo
|
|
38
|
+
*.h5ad
|
|
39
|
+
*.h5
|
|
40
|
+
*.hdf5
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Contributing to annslicer
|
|
2
|
+
|
|
3
|
+
## Development setup
|
|
4
|
+
|
|
5
|
+
We recommend working inside a conda environment to keep dependencies isolated.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
conda create -n annslicer python=3.10
|
|
9
|
+
conda activate annslicer
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Clone the repo:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/cellarium-ai/annslicer.git
|
|
16
|
+
cd annslicer
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
And then install in editable mode with development dependencies. You can then either install using the Makefile command
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
make install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
or instead, you can equivalently run
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install --upgrade pip
|
|
29
|
+
pip install -e ".[dev]"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The `annslicer` command will now point to your local source, and changes take effect immediately without reinstalling.
|
|
33
|
+
|
|
34
|
+
## Running tests
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pytest
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Zarr-related tests (zarr output merging, zarr input slicing, zarr shuffle) are skipped automatically if `zarr` is not installed.
|
|
41
|
+
To run the full test suite including zarr:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install -e ".[dev,zarr]"
|
|
45
|
+
pytest
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Linting, formatting, and type-checking
|
|
49
|
+
|
|
50
|
+
Before committing, run two commands from the root of the repo:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
make lint
|
|
54
|
+
make typecheck
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or if you want to type things manually:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Check for lint errors
|
|
61
|
+
ruff check src/ tests/
|
|
62
|
+
|
|
63
|
+
# Auto-fix where possible
|
|
64
|
+
ruff check --fix src/ tests/
|
|
65
|
+
|
|
66
|
+
# Check formatting
|
|
67
|
+
ruff format --check src/ tests/
|
|
68
|
+
|
|
69
|
+
# Apply formatting
|
|
70
|
+
ruff format src/ tests/
|
|
71
|
+
|
|
72
|
+
# Type-check
|
|
73
|
+
mypy src/annslicer
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
All three checks run automatically on every push and pull request via GitHub Actions.
|
|
77
|
+
|
|
78
|
+
## Running benchmarks
|
|
79
|
+
|
|
80
|
+
Benchmarks live in `benchmarks/` and are excluded from the normal `pytest` run so that CI stays fast. Run them locally with:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
make benchmark
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or directly:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pytest benchmarks/ --benchmark-only -v
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The benchmark suite (`benchmarks/bench_slice.py`) compares:
|
|
93
|
+
|
|
94
|
+
| Benchmark | What it measures |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `bench_annslicer_slice` | Full out-of-core sharding pipeline (no shuffle) |
|
|
97
|
+
| `bench_annslicer_slice_shuffle` | Overhead of random shuffling via sort-read-reorder |
|
|
98
|
+
| `bench_anndata_backed_iterate` | Baseline: backed AnnData row iteration |
|
|
99
|
+
|
|
100
|
+
Adjust `N_CELLS_BENCH` and `N_GENES_BENCH` in `benchmarks/conftest.py` to scale the dataset up or down.
|
|
101
|
+
|
|
102
|
+
## Releasing a new version
|
|
103
|
+
|
|
104
|
+
Version is derived automatically from git tags — there is no version string to update in code.
|
|
105
|
+
|
|
106
|
+
1. Ensure all tests and lint checks pass on `main`.
|
|
107
|
+
2. Tag the release commit:
|
|
108
|
+
```bash
|
|
109
|
+
git tag v0.2.0
|
|
110
|
+
git push --tags
|
|
111
|
+
```
|
|
112
|
+
3. In the GitHub Actions tab, manually trigger the **Publish to PyPI** workflow.
|
|
113
|
+
|
|
114
|
+
That's it. `setuptools-scm` picks the version from the tag, builds the sdist and wheel, and publishes to PyPI using OIDC Trusted Publishing (no API token required).
|
|
115
|
+
|
|
116
|
+
## Project layout
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
src/annslicer/
|
|
120
|
+
__init__.py — public API surface and __version__
|
|
121
|
+
cli.py — unified entry point; registers slice and merge subcommands
|
|
122
|
+
_store.py — shared HDF5/Zarr store helpers (open_store, _is_sparse_group, _require_zarr)
|
|
123
|
+
slice.py — shard logic: shard_h5ad (h5ad + zarr input, shuffle), register_subcommand
|
|
124
|
+
merge.py — merge logic: merge_out_of_core (h5ad + zarr output), register_subcommand
|
|
125
|
+
tests/
|
|
126
|
+
conftest.py — synthetic .h5ad and .zarr fixtures
|
|
127
|
+
test_slice.py — unit and integration tests for slicing (including shuffle + zarr input)
|
|
128
|
+
test_merge.py — unit and integration tests for merging (h5ad + zarr)
|
|
129
|
+
benchmarks/
|
|
130
|
+
conftest.py — large synthetic fixture (configurable N_CELLS_BENCH × N_GENES_BENCH)
|
|
131
|
+
bench_slice.py — annslicer slice vs. backed AnnData iteration benchmarks
|
|
132
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Copyright (c) 2026, Broad Institute
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are met:
|
|
5
|
+
|
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
7
|
+
list of conditions and the following disclaimer.
|
|
8
|
+
|
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
and/or other materials provided with the distribution.
|
|
12
|
+
|
|
13
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
14
|
+
contributors may be used to endorse or promote products derived from
|
|
15
|
+
this software without specific prior written permission.
|
|
16
|
+
|
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
18
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
19
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
20
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
21
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
22
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
23
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
24
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
25
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
annslicer-0.1.3/Makefile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.PHONY: install lint typecheck benchmark build-check
|
|
2
|
+
|
|
3
|
+
install:
|
|
4
|
+
pip install --upgrade pip
|
|
5
|
+
pip install -e ".[dev]"
|
|
6
|
+
|
|
7
|
+
lint:
|
|
8
|
+
ruff check --fix src/ tests/ benchmarks/
|
|
9
|
+
ruff format src/ tests/ benchmarks/
|
|
10
|
+
|
|
11
|
+
typecheck:
|
|
12
|
+
mypy src/annslicer
|
|
13
|
+
|
|
14
|
+
benchmark:
|
|
15
|
+
pytest benchmarks/ --benchmark-only -v -s
|
|
16
|
+
|
|
17
|
+
build-check:
|
|
18
|
+
@echo "--- Building sdist and wheel ---"
|
|
19
|
+
python -m build --outdir /tmp/annslicer-dist-check
|
|
20
|
+
@echo "--- Checking distributions ---"
|
|
21
|
+
twine check /tmp/annslicer-dist-check/*
|
|
22
|
+
@rm -rf /tmp/annslicer-dist-check
|
|
23
|
+
@echo "--- Build check passed ---"
|
annslicer-0.1.3/PKG-INFO
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: annslicer
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Out-of-core sharding of large .h5ad AnnData files with minimal memory usage.
|
|
5
|
+
Author: sfleming
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sfleming/annslicer
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/sfleming/annslicer/issues
|
|
9
|
+
Keywords: anndata,h5ad,bioinformatics,single-cell,genomics
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE.md
|
|
21
|
+
Requires-Dist: anndata>=0.9
|
|
22
|
+
Requires-Dist: numpy>=1.24
|
|
23
|
+
Requires-Dist: scipy>=1.10
|
|
24
|
+
Requires-Dist: h5py>=3.8
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: annslicer[zarr]; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-benchmark>=4.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy==1.15.0; extra == "dev"
|
|
31
|
+
Requires-Dist: build; extra == "dev"
|
|
32
|
+
Requires-Dist: twine; extra == "dev"
|
|
33
|
+
Provides-Extra: zarr
|
|
34
|
+
Requires-Dist: zarr>=2.10; extra == "zarr"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# annslicer
|
|
38
|
+
|
|
39
|
+
**Out-of-core sharding and merging of large AnnData files with minimal memory usage.**
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
Large single-cell datasets stored as `.h5ad` or `.zarr` files can easily exceed available RAM. `annslicer` slices them into manageable shards — and merges them back — without loading full matrices into memory. It uses best practices from `anndata` with a few small speed improvements for random shuffling.
|
|
44
|
+
|
|
45
|
+
Consolidates best practices into a simple command-line tool.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
annslicer slice input.h5ad output_prefix
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
annslicer merge output.h5ad shard_0.h5ad shard_1.h5ad
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- Shards and merges `X`, all `layers`, `obs`, `var`, `obsm`, and `uns`
|
|
58
|
+
- Handles both dense and sparse (CSR) matrices
|
|
59
|
+
- Constant, low memory footprint regardless of file size
|
|
60
|
+
- Input supports both `.h5ad` and `.zarr` formats for slicing
|
|
61
|
+
- Merge output supports both `.h5ad` and `.zarr` formats
|
|
62
|
+
- Optional **cell shuffling** (`--shuffle`) for representative shards without loading the full matrix
|
|
63
|
+
- Simple CLI and Python API
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install annslicer
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For Zarr input/output support (optional):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install annslicer[zarr]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## CLI Usage
|
|
78
|
+
|
|
79
|
+
`annslicer` provides two subcommands: `slice` and `merge`.
|
|
80
|
+
|
|
81
|
+
### Sharding a large file
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
annslicer slice input.h5ad output_prefix --size 10000
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Both `.h5ad` and `.zarr` inputs are supported.
|
|
88
|
+
|
|
89
|
+
| Argument | Description |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `input.h5ad` or `input.zarr` | Path to the source file |
|
|
92
|
+
| `output_prefix` | Prefix for output files (e.g. `atlas` → `atlas_shard001.h5ad`, …) |
|
|
93
|
+
| `--size N` | Number of cells per shard (default: `10000`) |
|
|
94
|
+
| `--shuffle` | Randomly assign cells to shards (each shard is a representative draw) |
|
|
95
|
+
| `--seed N` | Random seed for reproducible shuffling (requires `--shuffle`) |
|
|
96
|
+
|
|
97
|
+
**Example — basic sharding:**
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
annslicer slice /data/large_atlas.h5ad /outputs/atlas --size 20000
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Example — shuffled sharding from a large h5ad:**
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
annslicer slice /data/large_atlas.h5ad /outputs/atlas --size 10000 --shuffle --seed 0
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Produces: `atlas_shard_0.h5ad`, `atlas_shard_1.h5ad`, …
|
|
110
|
+
|
|
111
|
+
### Merging shards back into one file
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
annslicer merge output.h5ad shard_0.h5ad shard_1.h5ad shard_2.h5ad
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Output format is inferred from the extension — use `.zarr` for Zarr output (requires `annslicer[zarr]`):
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
annslicer merge output.zarr shard_0.h5ad shard_1.h5ad shard_2.h5ad
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Global options
|
|
124
|
+
|
|
125
|
+
| Flag | Description |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `--debug` | Enable verbose debug-level logging |
|
|
128
|
+
|
|
129
|
+
## Python API
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from annslicer import shard_h5ad, merge_out_of_core
|
|
133
|
+
|
|
134
|
+
# Basic sharding (h5ad or zarr input)
|
|
135
|
+
shard_h5ad("large_atlas.h5ad", "atlas", shard_size=20000)
|
|
136
|
+
shard_h5ad("large_atlas.zarr", "atlas", shard_size=20000) # requires annslicer[zarr]
|
|
137
|
+
|
|
138
|
+
# Shuffled sharding — cells are randomly distributed across shards
|
|
139
|
+
shard_h5ad("large_atlas.h5ad", "atlas", shard_size=20000, shuffle=True, seed=0)
|
|
140
|
+
|
|
141
|
+
# Merge shards back into one file
|
|
142
|
+
merge_out_of_core(["atlas_shard_0.h5ad", "atlas_shard_1.h5ad"], "merged.h5ad")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## How it works
|
|
146
|
+
|
|
147
|
+
### Slicing
|
|
148
|
+
1. Opens the input file ("backed" AnnData for `.h5ad`; `anndata.io.sparse_dataset` for `.zarr`).
|
|
149
|
+
2. If `shuffle=True`, generates a global cell permutation upfront using `numpy.random.default_rng`.
|
|
150
|
+
3. For each shard, reads only the relevant rows from `X` and each layer via sorted fancy indexing — no full matrix is ever loaded into memory.
|
|
151
|
+
4. When shuffling, rows are read in sorted index order (maximising sequential I/O) and then reordered in-memory to the desired shuffled order.
|
|
152
|
+
5. Reassembles a valid `AnnData` object per shard and writes it to disk.
|
|
153
|
+
|
|
154
|
+
### Merging
|
|
155
|
+
1. Reads `obs`, `var`, and `uns` from the shards to build a skeleton output file.
|
|
156
|
+
2. Scans shards to calculate total non-zero sizes for pre-allocation.
|
|
157
|
+
3. Streams `X`, layers, and `obsm` data shard-by-shard directly into the pre-allocated output arrays.
|
|
158
|
+
|
|
159
|
+
> **Note:** CSC (column-compressed) sparse matrices are not supported for out-of-core row-slicing. Convert to CSR before sharding.
|
|
160
|
+
|
|
161
|
+
## Benchmarks
|
|
162
|
+
|
|
163
|
+
Run on a dummy sparse anndata object with 200k cells and 10k genes.
|
|
164
|
+
|
|
165
|
+
### For h5ad format
|
|
166
|
+
|
|
167
|
+
| Slicing method | Mean runtime (s) | Peak memory (MB) |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `annslicer slice` | 0.584 | 211.4 |
|
|
170
|
+
| `anndata` backed | 0.601 | 203.7 |
|
|
171
|
+
| `annslicer slice --shuffle` | 1.731 | 221.8 |
|
|
172
|
+
| `anndata` backed with shuffle | 3.830 | 209.1 |
|
|
173
|
+
|
|
174
|
+
### For zarr format
|
|
175
|
+
|
|
176
|
+
| Slicing method | Mean runtime (s) | Peak memory (MB) |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `annslicer slice` | 1.050 | 62.1 |
|
|
179
|
+
| `anndata` backed | 0.799 | 54.4 |
|
|
180
|
+
| `annslicer slice --shuffle` | 5.544 | 142.9 |
|
|
181
|
+
| `anndata` backed with shuffle | 6.591 | 151.4 |
|
|
182
|
+
|
|
183
|
+
Based on these benchmarks, for making randomly shuffled data shards, we recommend using `annslicer slice --shuffle` on an h5ad format file.
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
BSD 3-clause
|