cellarr 0.4.0__tar.gz → 0.4.1.dev2__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.
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/.pre-commit-config.yaml +2 -2
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/PKG-INFO +43 -2
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/README.md +42 -1
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/setup.cfg +2 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/buildutils_tiledb_frame.py +1 -1
- cellarr-0.4.1.dev2/src/cellarr/slurm/README.md +65 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/__init__.py +0 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/build_cellarr_steps.py +319 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/final_assembly.py +29 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/finalize_matrix.py +45 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/process_cell_metadata.py +68 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/process_gene_annotation.py +45 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/process_matrix.py +58 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/process_matrix_all.py +40 -0
- cellarr-0.4.1.dev2/src/cellarr/slurm/process_sample_metadata.py +78 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/PKG-INFO +43 -2
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/SOURCES.txt +11 -0
- cellarr-0.4.1.dev2/src/cellarr.egg-info/entry_points.txt +2 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/.coveragerc +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/.github/workflows/pypi-publish.yml +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/.github/workflows/pypi-test.yml +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/.gitignore +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/.readthedocs.yml +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/AUTHORS.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/CHANGELOG.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/CONTRIBUTING.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/LICENSE.txt +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/assets/cellarr.jpg +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/assets/cellarr.png +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/assets/cellarr.svg +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/Makefile +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/_static/.gitignore +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/authors.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/changelog.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/conf.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/contributing.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/index.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/license.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/notes.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/readme.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/requirements.txt +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/docs/tutorial.md +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/pyproject.toml +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/setup.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/CellArrDataset.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/CellArrDatasetSlice.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/__init__.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/autoencoder.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/build_cellarrdataset.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/build_options.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/buildutils_tiledb_array.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/dataloader.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/queryutils_tiledb_frame.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr/utils_anndata.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/dependency_links.txt +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/not-zip-safe +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/requires.txt +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/top_level.txt +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/tests/conftest.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/tests/data/adata.h5ad +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/tests/test_anndata_utils.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/tests/test_build.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/tests/test_custom_config.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/tests/test_dataloader.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/tests/test_query.py +0 -0
- {cellarr-0.4.0 → cellarr-0.4.1.dev2}/tox.ini +0 -0
|
@@ -2,7 +2,7 @@ exclude: '^docs/conf.py'
|
|
|
2
2
|
|
|
3
3
|
repos:
|
|
4
4
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
5
|
-
rev:
|
|
5
|
+
rev: v5.0.0
|
|
6
6
|
hooks:
|
|
7
7
|
- id: trailing-whitespace
|
|
8
8
|
- id: check-added-large-files
|
|
@@ -33,7 +33,7 @@ repos:
|
|
|
33
33
|
|
|
34
34
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
35
35
|
# Ruff version.
|
|
36
|
-
rev: v0.
|
|
36
|
+
rev: v0.8.2
|
|
37
37
|
hooks:
|
|
38
38
|
- id: ruff
|
|
39
39
|
args: [--fix, --exit-non-zero-on-fix]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cellarr
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1.dev2
|
|
4
4
|
Summary: TileDB-based array storage for genomics data collections.
|
|
5
5
|
Home-page: https://github.com/BiocPy/cellarr
|
|
6
6
|
Author: Jayaram Kancherla
|
|
@@ -45,7 +45,7 @@ datasets but can be generalized to store any 2-dimensional experimental data.
|
|
|
45
45
|
|
|
46
46
|
To get started, install the package from [PyPI](https://pypi.org/project/cellarr/)
|
|
47
47
|
|
|
48
|
-
```
|
|
48
|
+
```sh
|
|
49
49
|
pip install cellarr
|
|
50
50
|
|
|
51
51
|
## to include optional dependencies
|
|
@@ -156,6 +156,47 @@ print(dataset)
|
|
|
156
156
|
|
|
157
157
|
Check out the [documentation](https://biocpy.github.io/cellarr/tutorial.html) for more details.
|
|
158
158
|
|
|
159
|
+
### Building on HPC environments with `slurm`
|
|
160
|
+
|
|
161
|
+
To simplify building TileDB files on HPC environments that use `slurm`, there are a few steps you need to follow.
|
|
162
|
+
|
|
163
|
+
- Step 1: Construct a manifest file
|
|
164
|
+
A minimal manifest file (json) must contain the following fields
|
|
165
|
+
- `"files"`: A list of file path to the input `h5ad` objects.
|
|
166
|
+
- `"python_env"`: A set of commands to activate the Python environment containing this package and its dependencies.
|
|
167
|
+
|
|
168
|
+
Here’s an example of the manifest file:
|
|
169
|
+
```json
|
|
170
|
+
manifest = {
|
|
171
|
+
"files": your/list/of/files,
|
|
172
|
+
"python_env": """
|
|
173
|
+
ml Miniforge3
|
|
174
|
+
conda activate cellarr
|
|
175
|
+
|
|
176
|
+
python --version
|
|
177
|
+
which python
|
|
178
|
+
""",
|
|
179
|
+
"matrix_options": [
|
|
180
|
+
{
|
|
181
|
+
"matrix_name": "non_zero_cells",
|
|
182
|
+
"dtype": "uint32"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"matrix_name": "pseudo_bulk_log_normed",
|
|
186
|
+
"dtype": "float32"
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
For more options, check out the [README](./src/cellarr/slurm/README.md).
|
|
192
|
+
|
|
193
|
+
- Step 2: Submit the job
|
|
194
|
+
Once your manifest file is ready, you can submit the necessary jobs using the `cellarr_build` CLI. Run the following command:
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
cellarr_build --input-manifest your/path/to/test_manifest.json --output-dir your/path/to/output --memory-per-job 8 --cpus-per-task 2
|
|
198
|
+
```
|
|
199
|
+
|
|
159
200
|
### Query a `CellArrDataset`
|
|
160
201
|
|
|
161
202
|
Users have the option to reuse the `dataset` object returned when building the dataset or by creating a `CellArrDataset` object by initializing it to the path where the files were created.
|
|
@@ -12,7 +12,7 @@ datasets but can be generalized to store any 2-dimensional experimental data.
|
|
|
12
12
|
|
|
13
13
|
To get started, install the package from [PyPI](https://pypi.org/project/cellarr/)
|
|
14
14
|
|
|
15
|
-
```
|
|
15
|
+
```sh
|
|
16
16
|
pip install cellarr
|
|
17
17
|
|
|
18
18
|
## to include optional dependencies
|
|
@@ -123,6 +123,47 @@ print(dataset)
|
|
|
123
123
|
|
|
124
124
|
Check out the [documentation](https://biocpy.github.io/cellarr/tutorial.html) for more details.
|
|
125
125
|
|
|
126
|
+
### Building on HPC environments with `slurm`
|
|
127
|
+
|
|
128
|
+
To simplify building TileDB files on HPC environments that use `slurm`, there are a few steps you need to follow.
|
|
129
|
+
|
|
130
|
+
- Step 1: Construct a manifest file
|
|
131
|
+
A minimal manifest file (json) must contain the following fields
|
|
132
|
+
- `"files"`: A list of file path to the input `h5ad` objects.
|
|
133
|
+
- `"python_env"`: A set of commands to activate the Python environment containing this package and its dependencies.
|
|
134
|
+
|
|
135
|
+
Here’s an example of the manifest file:
|
|
136
|
+
```json
|
|
137
|
+
manifest = {
|
|
138
|
+
"files": your/list/of/files,
|
|
139
|
+
"python_env": """
|
|
140
|
+
ml Miniforge3
|
|
141
|
+
conda activate cellarr
|
|
142
|
+
|
|
143
|
+
python --version
|
|
144
|
+
which python
|
|
145
|
+
""",
|
|
146
|
+
"matrix_options": [
|
|
147
|
+
{
|
|
148
|
+
"matrix_name": "non_zero_cells",
|
|
149
|
+
"dtype": "uint32"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"matrix_name": "pseudo_bulk_log_normed",
|
|
153
|
+
"dtype": "float32"
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
For more options, check out the [README](./src/cellarr/slurm/README.md).
|
|
159
|
+
|
|
160
|
+
- Step 2: Submit the job
|
|
161
|
+
Once your manifest file is ready, you can submit the necessary jobs using the `cellarr_build` CLI. Run the following command:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
cellarr_build --input-manifest your/path/to/test_manifest.json --output-dir your/path/to/output --memory-per-job 8 --cpus-per-task 2
|
|
165
|
+
```
|
|
166
|
+
|
|
126
167
|
### Query a `CellArrDataset`
|
|
127
168
|
|
|
128
169
|
Users have the option to reuse the `dataset` object returned when building the dataset or by creating a `CellArrDataset` object by initializing it to the path where the files were created.
|
|
@@ -76,7 +76,7 @@ def create_tiledb_frame_from_column_names(
|
|
|
76
76
|
)
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def create_tiledb_frame_from_dataframe(tiledb_uri_path: str, frame: List[str], column_types=
|
|
79
|
+
def create_tiledb_frame_from_dataframe(tiledb_uri_path: str, frame: List[str], column_types: dict = None):
|
|
80
80
|
"""Create a TileDB file with the provided attributes to persistent storage.
|
|
81
81
|
|
|
82
82
|
This will materialize the array directory and all
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
|
|
2
|
+
# manifest json
|
|
3
|
+
|
|
4
|
+
```json
|
|
5
|
+
{
|
|
6
|
+
"files": [
|
|
7
|
+
"/path/to/dataset1.h5ad",
|
|
8
|
+
"/path/to/dataset2.h5ad"
|
|
9
|
+
],
|
|
10
|
+
"matrix_options": [
|
|
11
|
+
{
|
|
12
|
+
"matrix_name": "counts",
|
|
13
|
+
"dtype": "uint32"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"matrix_name": "normalized",
|
|
17
|
+
"dtype": "float32"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"gene_options": {
|
|
21
|
+
"feature_column": "index"
|
|
22
|
+
},
|
|
23
|
+
"sample_options": {
|
|
24
|
+
"metadata": {
|
|
25
|
+
"sample_1": {
|
|
26
|
+
"condition": "control",
|
|
27
|
+
"batch": "1"
|
|
28
|
+
},
|
|
29
|
+
"sample_2": {
|
|
30
|
+
"condition": "treatment",
|
|
31
|
+
"batch": "1"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"cell_options": {
|
|
36
|
+
"column_types": {
|
|
37
|
+
"cell_type": "ascii",
|
|
38
|
+
"quality_score": "float32"
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
"python_env": """
|
|
42
|
+
. /system/gredit/clientos/etc/profile
|
|
43
|
+
|
|
44
|
+
ml Miniforge3
|
|
45
|
+
conda activate biocpy_miniforge
|
|
46
|
+
|
|
47
|
+
~/.conda/envs/biocpy_miniforge/bin/python --version
|
|
48
|
+
which python
|
|
49
|
+
python --version
|
|
50
|
+
""",
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
Run
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
|
|
59
|
+
python build_cellarr_steps.py \
|
|
60
|
+
--input-manifest manifest.json \
|
|
61
|
+
--output-dir /path/to/output \
|
|
62
|
+
--memory-per-job 64 \
|
|
63
|
+
--cpus-per-task 4
|
|
64
|
+
|
|
65
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, List, Optional, Tuple
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from cellarr.buildutils_tiledb_array import create_tiledb_array
|
|
10
|
+
|
|
11
|
+
__author__ = "Jayaram Kancherla"
|
|
12
|
+
__copyright__ = "Jayaram Kancherla"
|
|
13
|
+
__license__ = "MIT"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SlurmBuilder:
|
|
17
|
+
"""SLURM-based builder for CellArrDataset."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
output_dir: str,
|
|
22
|
+
log_dir: str,
|
|
23
|
+
temp_dir: str,
|
|
24
|
+
memory_gb: int = 64,
|
|
25
|
+
time_hours: int = 24,
|
|
26
|
+
cpus_per_task: int = 4,
|
|
27
|
+
):
|
|
28
|
+
"""Initialize the SLURM builder.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
output_dir:
|
|
32
|
+
Path to final output directory.
|
|
33
|
+
|
|
34
|
+
log_dir:
|
|
35
|
+
Path to store SLURM logs.
|
|
36
|
+
|
|
37
|
+
temp_dir:
|
|
38
|
+
Path for temporary files.
|
|
39
|
+
|
|
40
|
+
memory_gb:
|
|
41
|
+
Memory per job in GB.
|
|
42
|
+
|
|
43
|
+
time_hours:
|
|
44
|
+
Time limit per job in hours.
|
|
45
|
+
|
|
46
|
+
cpus_per_task:
|
|
47
|
+
CPUs per task.
|
|
48
|
+
"""
|
|
49
|
+
self.output_dir = Path(output_dir)
|
|
50
|
+
self.log_dir = Path(log_dir)
|
|
51
|
+
self.temp_dir = Path(temp_dir)
|
|
52
|
+
self.memory_gb = memory_gb
|
|
53
|
+
self.time_hours = time_hours
|
|
54
|
+
self.cpus_per_task = cpus_per_task
|
|
55
|
+
|
|
56
|
+
def create_slurm_script(
|
|
57
|
+
self,
|
|
58
|
+
job_name: str,
|
|
59
|
+
python_script: str,
|
|
60
|
+
args: Dict,
|
|
61
|
+
dependencies: Optional[str] = None,
|
|
62
|
+
python_env: str = "",
|
|
63
|
+
) -> str:
|
|
64
|
+
"""Create a SLURM job submission script."""
|
|
65
|
+
script = f"""#!/bin/bash
|
|
66
|
+
#SBATCH --job-name={job_name}
|
|
67
|
+
#SBATCH --output={self.log_dir}/{job_name}_%j.out
|
|
68
|
+
#SBATCH --error={self.log_dir}/{job_name}_%j.err
|
|
69
|
+
#SBATCH --time={self.time_hours}:00:00
|
|
70
|
+
#SBATCH --mem={self.memory_gb}G
|
|
71
|
+
#SBATCH --cpus-per-task={self.cpus_per_task}
|
|
72
|
+
"""
|
|
73
|
+
if dependencies:
|
|
74
|
+
script += f"#SBATCH --dependency={dependencies}\n"
|
|
75
|
+
|
|
76
|
+
script += f"""
|
|
77
|
+
{python_env}
|
|
78
|
+
|
|
79
|
+
python {python_script} '{json.dumps(args)}'
|
|
80
|
+
"""
|
|
81
|
+
script_path = self.log_dir / f"{job_name}_submit.sh"
|
|
82
|
+
with open(script_path, "w") as f:
|
|
83
|
+
f.write(script)
|
|
84
|
+
return script_path
|
|
85
|
+
|
|
86
|
+
def create_array_script(
|
|
87
|
+
self,
|
|
88
|
+
job_name: str,
|
|
89
|
+
python_script: str,
|
|
90
|
+
args: Dict,
|
|
91
|
+
n_tasks: int,
|
|
92
|
+
dependencies: Optional[str] = None,
|
|
93
|
+
python_env: str = "",
|
|
94
|
+
) -> str:
|
|
95
|
+
"""Create a SLURM array job submission script."""
|
|
96
|
+
script = f"""#!/bin/bash
|
|
97
|
+
#SBATCH --job-name={job_name}
|
|
98
|
+
#SBATCH --output={self.log_dir}/{job_name}_%A_%a.out
|
|
99
|
+
#SBATCH --error={self.log_dir}/{job_name}_%A_%a.err
|
|
100
|
+
#SBATCH --time={self.time_hours}:00:00
|
|
101
|
+
#SBATCH --mem={self.memory_gb}G
|
|
102
|
+
#SBATCH --cpus-per-task={self.cpus_per_task}
|
|
103
|
+
#SBATCH --array=0-{n_tasks-1}
|
|
104
|
+
"""
|
|
105
|
+
if dependencies:
|
|
106
|
+
script += f"#SBATCH --dependency={dependencies}\n"
|
|
107
|
+
|
|
108
|
+
script += f"""
|
|
109
|
+
{python_env}
|
|
110
|
+
|
|
111
|
+
python {python_script} '{json.dumps(args)}'
|
|
112
|
+
"""
|
|
113
|
+
script_path = self.log_dir / f"{job_name}_array.sh"
|
|
114
|
+
with open(script_path, "w") as f:
|
|
115
|
+
f.write(script)
|
|
116
|
+
return script_path
|
|
117
|
+
|
|
118
|
+
def submit_job(self, script_path: str) -> str:
|
|
119
|
+
"""Submit a SLURM job and return job ID."""
|
|
120
|
+
result = subprocess.run(["sbatch", script_path], capture_output=True, text=True)
|
|
121
|
+
return result.stdout.strip().split()[-1]
|
|
122
|
+
|
|
123
|
+
def submit_gene_annotation_job(self, files: List[str], gene_options: Dict, python_env: str) -> str:
|
|
124
|
+
"""Submit gene annotation processing job."""
|
|
125
|
+
args = {
|
|
126
|
+
"files": files,
|
|
127
|
+
"output_dir": str(self.output_dir),
|
|
128
|
+
"gene_options": gene_options,
|
|
129
|
+
"temp_dir": str(self.temp_dir / "gene_annotation"),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
parent_dir = str(Path(__file__).parent)
|
|
133
|
+
|
|
134
|
+
script_path = self.create_slurm_script(
|
|
135
|
+
job_name="cellarr_gene_annot",
|
|
136
|
+
python_script=f"{parent_dir}/process_gene_annotation.py",
|
|
137
|
+
args=args,
|
|
138
|
+
python_env=python_env,
|
|
139
|
+
)
|
|
140
|
+
return self.submit_job(script_path)
|
|
141
|
+
|
|
142
|
+
def submit_sample_metadata_job(
|
|
143
|
+
self, files: List[str], sample_options: Dict, dependency: str, python_env: str
|
|
144
|
+
) -> str:
|
|
145
|
+
"""Submit sample metadata processing job."""
|
|
146
|
+
args = {
|
|
147
|
+
"files": files,
|
|
148
|
+
"output_dir": str(self.output_dir),
|
|
149
|
+
"sample_options": sample_options,
|
|
150
|
+
"temp_dir": str(self.temp_dir / "sample_metadata"),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
parent_dir = str(Path(__file__).parent)
|
|
154
|
+
|
|
155
|
+
script_path = self.create_slurm_script(
|
|
156
|
+
job_name="cellarr_sample_meta",
|
|
157
|
+
python_script=f"{parent_dir}/process_sample_metadata.py",
|
|
158
|
+
args=args,
|
|
159
|
+
python_env=python_env,
|
|
160
|
+
# dependencies=f"afterok:{dependency}",
|
|
161
|
+
)
|
|
162
|
+
return self.submit_job(script_path)
|
|
163
|
+
|
|
164
|
+
def submit_cell_metadata_job(self, files: List[str], cell_options: Dict, dependency: str, python_env: str) -> str:
|
|
165
|
+
"""Submit cell metadata processing job."""
|
|
166
|
+
args = {
|
|
167
|
+
"files": files,
|
|
168
|
+
"output_dir": str(self.output_dir),
|
|
169
|
+
"cell_options": cell_options,
|
|
170
|
+
"temp_dir": str(self.temp_dir / "cell_metadata"),
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
parent_dir = str(Path(__file__).parent)
|
|
174
|
+
|
|
175
|
+
script_path = self.create_slurm_script(
|
|
176
|
+
job_name="cellarr_cell_meta",
|
|
177
|
+
python_script=f"{parent_dir}/process_cell_metadata.py",
|
|
178
|
+
args=args,
|
|
179
|
+
python_env=python_env,
|
|
180
|
+
# dependencies=f"afterok:{dependency}",
|
|
181
|
+
)
|
|
182
|
+
return self.submit_job(script_path)
|
|
183
|
+
|
|
184
|
+
def submit_matrix_processing(
|
|
185
|
+
self, files: List[str], matrix_options: Dict, dependency: str, python_env: str
|
|
186
|
+
) -> Tuple[str, str]:
|
|
187
|
+
"""Submit matrix processing as SLURM array job."""
|
|
188
|
+
|
|
189
|
+
# Create matrix TileDB array
|
|
190
|
+
matrix_uri = str(self.output_dir / "assays" / matrix_options["matrix_name"])
|
|
191
|
+
create_tiledb_array(
|
|
192
|
+
matrix_uri,
|
|
193
|
+
matrix_attr_name=matrix_options.get("matrix_attr_name", "data"),
|
|
194
|
+
matrix_dim_dtype=np.dtype(matrix_options.get("dtype", "float32")),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Prepare array job arguments
|
|
198
|
+
array_args = {
|
|
199
|
+
"output_dir": str(self.output_dir),
|
|
200
|
+
"temp_dir": str(self.temp_dir / f"matrix_{matrix_options['matrix_name']}"),
|
|
201
|
+
"matrix_options": matrix_options,
|
|
202
|
+
"gene_annotation_file": str(self.temp_dir / "gene_annotation/gene_set.json"),
|
|
203
|
+
"files": files,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
parent_dir = str(Path(__file__).parent)
|
|
207
|
+
|
|
208
|
+
# Submit array job
|
|
209
|
+
array_script = self.create_array_script(
|
|
210
|
+
job_name=f"matrix_{matrix_options['matrix_name']}",
|
|
211
|
+
python_script=f"{parent_dir}/process_matrix.py",
|
|
212
|
+
args=array_args,
|
|
213
|
+
n_tasks=len(files),
|
|
214
|
+
dependencies=f"afterok:{dependency}",
|
|
215
|
+
python_env=python_env,
|
|
216
|
+
)
|
|
217
|
+
array_job_id = self.submit_job(array_script)
|
|
218
|
+
|
|
219
|
+
# Submit finalization job
|
|
220
|
+
final_args = {
|
|
221
|
+
"output_dir": str(self.output_dir),
|
|
222
|
+
"temp_dir": str(self.temp_dir / f"matrix_{matrix_options['matrix_name']}"),
|
|
223
|
+
"matrix_options": matrix_options,
|
|
224
|
+
"files": files,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
final_script = self.create_slurm_script(
|
|
228
|
+
job_name=f"finalize_matrix_{matrix_options['matrix_name']}",
|
|
229
|
+
python_script=f"{parent_dir}/finalize_matrix.py",
|
|
230
|
+
args=final_args,
|
|
231
|
+
dependencies=f"afterok:{array_job_id}",
|
|
232
|
+
python_env=python_env,
|
|
233
|
+
)
|
|
234
|
+
final_job_id = self.submit_job(final_script)
|
|
235
|
+
|
|
236
|
+
return array_job_id, final_job_id
|
|
237
|
+
|
|
238
|
+
def submit_final_assembly(self, matrix_names: List[str], dependencies: List[str], python_env: str) -> str:
|
|
239
|
+
"""Submit final assembly job."""
|
|
240
|
+
args = {"input_dir": str(self.output_dir), "output_dir": str(self.output_dir), "matrix_names": matrix_names}
|
|
241
|
+
|
|
242
|
+
parent_dir = str(Path(__file__).parent)
|
|
243
|
+
|
|
244
|
+
script_path = self.create_slurm_script(
|
|
245
|
+
job_name="cellarr_final_assembly",
|
|
246
|
+
python_script=f"{parent_dir}/final_assembly.py",
|
|
247
|
+
args=args,
|
|
248
|
+
dependencies=f"afterok:{','.join(dependencies)}",
|
|
249
|
+
python_env=python_env,
|
|
250
|
+
)
|
|
251
|
+
return self.submit_job(script_path)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def main():
|
|
255
|
+
parser = argparse.ArgumentParser(description="Build CellArrDataset using SLURM steps")
|
|
256
|
+
parser.add_argument("--input-manifest", required=True, help="Path to JSON manifest file")
|
|
257
|
+
parser.add_argument("--output-dir", required=True, help="Base output directory")
|
|
258
|
+
parser.add_argument("--memory-per-job", type=int, default=64, help="Memory in GB per job")
|
|
259
|
+
parser.add_argument("--time-per-job", type=int, default=24, help="Time in hours per job")
|
|
260
|
+
parser.add_argument("--cpus-per-task", type=int, default=4, help="CPUs per task")
|
|
261
|
+
args = parser.parse_args()
|
|
262
|
+
|
|
263
|
+
# Create directories
|
|
264
|
+
base_dir = Path(args.output_dir)
|
|
265
|
+
log_dir = base_dir / "logs"
|
|
266
|
+
temp_dir = base_dir / "temp"
|
|
267
|
+
final_dir = base_dir / "final"
|
|
268
|
+
assays_dir = base_dir / "final/assays"
|
|
269
|
+
|
|
270
|
+
for d in [log_dir, temp_dir, final_dir, assays_dir]:
|
|
271
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
272
|
+
|
|
273
|
+
# Read manifest
|
|
274
|
+
with open(args.input_manifest) as f:
|
|
275
|
+
manifest = json.load(f)
|
|
276
|
+
|
|
277
|
+
# Initialize builder
|
|
278
|
+
builder = SlurmBuilder(
|
|
279
|
+
output_dir=str(final_dir),
|
|
280
|
+
log_dir=str(log_dir),
|
|
281
|
+
temp_dir=str(temp_dir),
|
|
282
|
+
memory_gb=args.memory_per_job,
|
|
283
|
+
time_hours=args.time_per_job,
|
|
284
|
+
cpus_per_task=args.cpus_per_task,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# Submit jobs
|
|
288
|
+
gene_job_id = builder.submit_gene_annotation_job(
|
|
289
|
+
manifest["files"], manifest.get("gene_options", {}), manifest["python_env"]
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
sample_job_id = builder.submit_sample_metadata_job(
|
|
293
|
+
manifest["files"], manifest.get("sample_options", {}), gene_job_id, manifest["python_env"]
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
cell_job_id = builder.submit_cell_metadata_job(
|
|
297
|
+
manifest["files"], manifest.get("cell_options", {}), sample_job_id, manifest["python_env"]
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# Process matrices
|
|
301
|
+
matrix_options = manifest.get("matrix_options", [{"matrix_name": "counts"}])
|
|
302
|
+
if not isinstance(matrix_options, list):
|
|
303
|
+
matrix_options = [matrix_options]
|
|
304
|
+
|
|
305
|
+
matrix_job_ids = []
|
|
306
|
+
for matrix_opt in matrix_options:
|
|
307
|
+
_, final_id = builder.submit_matrix_processing(
|
|
308
|
+
manifest["files"], matrix_opt, f"{cell_job_id},{gene_job_id}", manifest["python_env"]
|
|
309
|
+
)
|
|
310
|
+
matrix_job_ids.append(final_id)
|
|
311
|
+
|
|
312
|
+
# Submit final assembly
|
|
313
|
+
builder.submit_final_assembly(
|
|
314
|
+
[opt["matrix_name"] for opt in matrix_options], matrix_job_ids, manifest["python_env"]
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
if __name__ == "__main__":
|
|
319
|
+
main()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from cellarr import CellArrDataset
|
|
5
|
+
|
|
6
|
+
__author__ = "Jayaram Kancherla"
|
|
7
|
+
__copyright__ = "Jayaram Kancherla"
|
|
8
|
+
__license__ = "MIT"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def final_assembly(args_json: str):
|
|
12
|
+
args = json.loads(args_json)
|
|
13
|
+
|
|
14
|
+
# Perform any final optimizations or validations
|
|
15
|
+
dataset = CellArrDataset(dataset_path=args["output_dir"], assay_uri=args["matrix_names"])
|
|
16
|
+
|
|
17
|
+
# Save final metadata
|
|
18
|
+
metadata = {"shape": dataset.shape, "matrices": args["matrix_names"]}
|
|
19
|
+
|
|
20
|
+
with open(f"{args['output_dir']}/metadata.json", "w") as f:
|
|
21
|
+
json.dump(metadata, f)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
if len(sys.argv) != 2:
|
|
26
|
+
print("Usage: python final_assembly.py '<json_args>'")
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
|
|
29
|
+
final_assembly(sys.argv[1])
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from cellarr.buildutils_tiledb_array import optimize_tiledb_array
|
|
6
|
+
|
|
7
|
+
__author__ = "Jayaram Kancherla"
|
|
8
|
+
__copyright__ = "Jayaram Kancherla"
|
|
9
|
+
__license__ = "MIT"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def finalize_matrix(args_json: str):
|
|
13
|
+
"""Finalize the matrix after all array jobs complete."""
|
|
14
|
+
args = json.loads(args_json)
|
|
15
|
+
|
|
16
|
+
# Verify all tasks completed
|
|
17
|
+
completed_dir = Path(args["temp_dir"]) / "completed"
|
|
18
|
+
expected_tasks = len(args["files"])
|
|
19
|
+
completed_tasks = len(list(completed_dir.glob("task_*.json")))
|
|
20
|
+
|
|
21
|
+
if completed_tasks != expected_tasks:
|
|
22
|
+
raise RuntimeError(f"Expected {expected_tasks} tasks but only {completed_tasks} completed")
|
|
23
|
+
|
|
24
|
+
# Optimize the TileDB array
|
|
25
|
+
matrix_uri = f"{args['output_dir']}/assays/{args['matrix_options']['matrix_name']}"
|
|
26
|
+
optimize_tiledb_array(matrix_uri)
|
|
27
|
+
|
|
28
|
+
# Save completion metadata
|
|
29
|
+
with open(f"{args['temp_dir']}/matrix_metadata.json", "w") as f:
|
|
30
|
+
json.dump(
|
|
31
|
+
{
|
|
32
|
+
"matrix_name": args["matrix_options"]["matrix_name"],
|
|
33
|
+
"files_processed": len(args["files"]),
|
|
34
|
+
"uri": matrix_uri,
|
|
35
|
+
},
|
|
36
|
+
f,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
if len(sys.argv) != 2:
|
|
42
|
+
print("Usage: python finalize_matrix.py '<json_args>'")
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
|
|
45
|
+
finalize_matrix(sys.argv[1])
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from cellarr import utils_anndata as uad
|
|
7
|
+
from cellarr.buildutils_tiledb_frame import (
|
|
8
|
+
create_tiledb_frame_from_dataframe,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__author__ = "Jayaram Kancherla"
|
|
12
|
+
__copyright__ = "Jayaram Kancherla"
|
|
13
|
+
__license__ = "MIT"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def process_cell_metadata(args_json: str):
|
|
17
|
+
"""Process and create cell metadata store.
|
|
18
|
+
|
|
19
|
+
Creates cell metadata including:
|
|
20
|
+
- Sample mapping
|
|
21
|
+
- Cell indices within samples
|
|
22
|
+
- Original cell annotations from input files
|
|
23
|
+
"""
|
|
24
|
+
args = json.loads(args_json)
|
|
25
|
+
|
|
26
|
+
# Extract cell metadata with specific column subset if provided
|
|
27
|
+
cell_meta_columns = args.get("cell_options", {}).get("column_types", {})
|
|
28
|
+
files_cache = uad.extract_anndata_info(
|
|
29
|
+
args["files"], obs_subset_columns=list(cell_meta_columns.keys()) if cell_meta_columns else None
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Get cell counts
|
|
33
|
+
cell_counts = uad.scan_for_cellcounts(files_cache)
|
|
34
|
+
|
|
35
|
+
# Create sample mapping for each cell
|
|
36
|
+
sample_per_cell = []
|
|
37
|
+
cell_index_in_sample = []
|
|
38
|
+
sample_names = [f"sample_{idx+1}" for idx in range(len(args["files"]))]
|
|
39
|
+
|
|
40
|
+
for idx, count in enumerate(cell_counts):
|
|
41
|
+
sample_per_cell.extend([sample_names[idx]] * count)
|
|
42
|
+
cell_index_in_sample.extend(range(count))
|
|
43
|
+
|
|
44
|
+
# Create base cell metadata
|
|
45
|
+
cell_metadata = pd.DataFrame(
|
|
46
|
+
{"cellarr_sample": sample_per_cell, "cellarr_cell_index_in_sample": cell_index_in_sample}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Add original cell annotations from input files
|
|
50
|
+
if cell_meta_columns:
|
|
51
|
+
original_meta = uad.scan_for_cellmetadata(files_cache)
|
|
52
|
+
if not original_meta.empty:
|
|
53
|
+
# Ensure index alignment
|
|
54
|
+
original_meta.reset_index(drop=True, inplace=True)
|
|
55
|
+
cell_metadata = pd.concat([cell_metadata, original_meta], axis=1)
|
|
56
|
+
|
|
57
|
+
# Create TileDB store
|
|
58
|
+
create_tiledb_frame_from_dataframe(
|
|
59
|
+
f"{args['output_dir']}/cell_metadata", cell_metadata, column_types=cell_meta_columns
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
if len(sys.argv) != 2:
|
|
65
|
+
print("Usage: python process_cell_metadata.py '<json_args>'")
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
|
|
68
|
+
process_cell_metadata(sys.argv[1])
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from cellarr import utils_anndata as uad
|
|
8
|
+
from cellarr.buildutils_tiledb_frame import create_tiledb_frame_from_dataframe
|
|
9
|
+
|
|
10
|
+
__author__ = "Jayaram Kancherla"
|
|
11
|
+
__copyright__ = "Jayaram Kancherla"
|
|
12
|
+
__license__ = "MIT"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def process_gene_annotation(args_json: str):
|
|
16
|
+
args = json.loads(args_json)
|
|
17
|
+
|
|
18
|
+
# Extract gene information from all files
|
|
19
|
+
files_cache = uad.extract_anndata_info(
|
|
20
|
+
args["files"], var_feature_column=args.get("gene_options", {}).get("feature_column", "index")
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Scan for features
|
|
24
|
+
gene_set = uad.scan_for_features(files_cache)
|
|
25
|
+
gene_set = sorted(gene_set)
|
|
26
|
+
|
|
27
|
+
# Create gene annotation dataframe
|
|
28
|
+
gene_annotation = pd.DataFrame({"cellarr_gene_index": gene_set}, index=gene_set)
|
|
29
|
+
gene_annotation.reset_index(drop=True, inplace=True)
|
|
30
|
+
|
|
31
|
+
# Create TileDB store
|
|
32
|
+
create_tiledb_frame_from_dataframe(f"{args['output_dir']}/gene_annotation", gene_annotation)
|
|
33
|
+
|
|
34
|
+
# Save gene set for later use
|
|
35
|
+
Path(args["temp_dir"]).mkdir(parents=True, exist_ok=True)
|
|
36
|
+
with open(f"{args['temp_dir']}/gene_set.json", "w") as f:
|
|
37
|
+
json.dump(gene_set, f)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
if len(sys.argv) != 2:
|
|
42
|
+
print("Usage: python process_gene_annotation.py '<json_args>'")
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
|
|
45
|
+
process_gene_annotation(sys.argv[1])
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import tiledb
|
|
7
|
+
|
|
8
|
+
from cellarr import utils_anndata as uad
|
|
9
|
+
from cellarr.buildutils_tiledb_array import write_csr_matrix_to_tiledb
|
|
10
|
+
|
|
11
|
+
__author__ = "Jayaram Kancherla"
|
|
12
|
+
__copyright__ = "Jayaram Kancherla"
|
|
13
|
+
__license__ = "MIT"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def process_matrix_file(args_json: str):
|
|
17
|
+
"""Process a single file for matrix creation."""
|
|
18
|
+
args = json.loads(args_json)
|
|
19
|
+
|
|
20
|
+
# Get SLURM array task ID
|
|
21
|
+
task_id = int(os.environ.get("SLURM_ARRAY_TASK_ID", 0))
|
|
22
|
+
|
|
23
|
+
# Load gene set mapping
|
|
24
|
+
with open(args["gene_annotation_file"]) as f:
|
|
25
|
+
gene_set = json.load(f)
|
|
26
|
+
gene_map = {gene: idx for idx, gene in enumerate(gene_set)}
|
|
27
|
+
|
|
28
|
+
# Get file and offset for this task
|
|
29
|
+
file_info = args["files"][task_id]
|
|
30
|
+
input_file = file_info
|
|
31
|
+
|
|
32
|
+
# get sample offset
|
|
33
|
+
sample_uri = tiledb.open(f"{args['output_dir']}/sample_metadata", "r")
|
|
34
|
+
sample_row = sample_uri.df[task_id]
|
|
35
|
+
row_offset = sample_row["cellarr_sample_start_index"]
|
|
36
|
+
|
|
37
|
+
# Process the file
|
|
38
|
+
matrix = uad.remap_anndata(input_file, gene_map, layer_matrix_name=args["matrix_options"]["matrix_name"])
|
|
39
|
+
|
|
40
|
+
# Write to TileDB
|
|
41
|
+
matrix_uri = f"{args['output_dir']}/assays/{args['matrix_options']['matrix_name']}"
|
|
42
|
+
write_csr_matrix_to_tiledb(matrix_uri, matrix[args["matrix_options"]["matrix_name"]], row_offset=row_offset)
|
|
43
|
+
|
|
44
|
+
# Save task completion marker
|
|
45
|
+
Path(args["temp_dir"]).mkdir(parents=True, exist_ok=True)
|
|
46
|
+
Path(args["temp_dir"] + "/completed").mkdir(parents=True, exist_ok=True)
|
|
47
|
+
task_marker = Path(args["temp_dir"]) / "completed" / f"task_{task_id}.json"
|
|
48
|
+
task_marker.parent.mkdir(exist_ok=True)
|
|
49
|
+
with open(task_marker, "w") as f:
|
|
50
|
+
json.dump({"file": input_file, "cells_processed": matrix[args["matrix_options"]["matrix_name"]].shape[0]}, f)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
if len(sys.argv) != 2:
|
|
55
|
+
print("Usage: python process_matrix.py '<json_args>'")
|
|
56
|
+
sys.exit(1)
|
|
57
|
+
|
|
58
|
+
process_matrix_file(sys.argv[1])
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from cellarr import utils_anndata as uad
|
|
6
|
+
from cellarr.buildutils_tiledb_array import create_tiledb_array, write_csr_matrix_to_tiledb
|
|
7
|
+
|
|
8
|
+
__author__ = "Jayaram Kancherla"
|
|
9
|
+
__copyright__ = "Jayaram Kancherla"
|
|
10
|
+
__license__ = "MIT"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def process_matrix(args_json: str):
|
|
14
|
+
args = json.loads(args_json)
|
|
15
|
+
|
|
16
|
+
# Load gene set
|
|
17
|
+
with open(args["gene_annotation_file"]) as f:
|
|
18
|
+
gene_set = json.load(f)
|
|
19
|
+
|
|
20
|
+
# Create gene set mapping
|
|
21
|
+
gene_map = {gene: idx for idx, gene in enumerate(gene_set)}
|
|
22
|
+
|
|
23
|
+
# Create TileDB array
|
|
24
|
+
create_tiledb_array(
|
|
25
|
+
f"{args['output_dir']}/assays/{args['matrix_options']['matrix_name']}",
|
|
26
|
+
matrix_dim_dtype=np.dtype(args["matrix_options"].get("dtype", "float32")),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Process each file
|
|
30
|
+
offset = 0
|
|
31
|
+
for file in args["files"]:
|
|
32
|
+
matrix = uad.remap_anndata(file, gene_map, layer_matrix_name=args["matrix_options"]["matrix_name"])
|
|
33
|
+
|
|
34
|
+
write_csr_matrix_to_tiledb(
|
|
35
|
+
f"{args['output_dir']}/assays/{args['matrix_options']['matrix_name']}",
|
|
36
|
+
matrix[args["matrix_options"]["matrix_name"]],
|
|
37
|
+
row_offset=offset,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
offset += matrix[args["matrix_options"]["matrix_name"]].shape[0]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from cellarr import utils_anndata as uad
|
|
8
|
+
from cellarr.buildutils_tiledb_frame import create_tiledb_frame_from_dataframe
|
|
9
|
+
|
|
10
|
+
__author__ = "Jayaram Kancherla"
|
|
11
|
+
__copyright__ = "Jayaram Kancherla"
|
|
12
|
+
__license__ = "MIT"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def process_sample_metadata(args_json: str):
|
|
16
|
+
"""Process and create sample metadata store.
|
|
17
|
+
|
|
18
|
+
Creates sample metadata including:
|
|
19
|
+
- Basic sample information
|
|
20
|
+
- Cell counts per sample
|
|
21
|
+
- Original gene sets
|
|
22
|
+
- Sample index information
|
|
23
|
+
"""
|
|
24
|
+
args = json.loads(args_json)
|
|
25
|
+
|
|
26
|
+
# Extract information from files
|
|
27
|
+
files_cache = uad.extract_anndata_info(args["files"], var_feature_column=args.get("feature_column", "index"))
|
|
28
|
+
|
|
29
|
+
# Get cell counts for each sample
|
|
30
|
+
cell_counts = uad.scan_for_cellcounts(files_cache)
|
|
31
|
+
|
|
32
|
+
# Create basic sample metadata
|
|
33
|
+
sample_names = [f"sample_{idx+1}" for idx in range(len(args["files"]))]
|
|
34
|
+
sample_metadata = pd.DataFrame(
|
|
35
|
+
{"cellarr_sample": sample_names, "cellarr_cell_counts": cell_counts, "cellarr_filename": args["files"]}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Add sample indices for efficient slicing
|
|
39
|
+
counter = sample_metadata["cellarr_cell_counts"].shift(1)
|
|
40
|
+
counter.iloc[0] = 0
|
|
41
|
+
sample_metadata["cellarr_sample_start_index"] = counter.cumsum().astype(int)
|
|
42
|
+
|
|
43
|
+
# Calculate end indices
|
|
44
|
+
ends = sample_metadata["cellarr_sample_start_index"].shift(-1)
|
|
45
|
+
ends.iloc[-1] = int(sample_metadata["cellarr_cell_counts"].sum())
|
|
46
|
+
ends = ends - 1
|
|
47
|
+
sample_metadata["cellarr_sample_end_index"] = ends.astype(int)
|
|
48
|
+
|
|
49
|
+
# Add original gene sets for each sample
|
|
50
|
+
gene_sets = uad.scan_for_features(files_cache, unique=False)
|
|
51
|
+
sample_metadata["cellarr_original_gene_set"] = [",".join(genes) for genes in gene_sets]
|
|
52
|
+
|
|
53
|
+
# Add any custom metadata if provided in options
|
|
54
|
+
custom_metadata = args.get("sample_options", {}).get("metadata", {})
|
|
55
|
+
for sample, metadata in custom_metadata.items():
|
|
56
|
+
for key, value in metadata.items():
|
|
57
|
+
if key not in sample_metadata.columns:
|
|
58
|
+
sample_metadata[key] = None
|
|
59
|
+
sample_idx = sample_metadata.index[sample_metadata["cellarr_sample"] == sample][0]
|
|
60
|
+
sample_metadata.at[sample_idx, key] = value
|
|
61
|
+
|
|
62
|
+
# Create TileDB store
|
|
63
|
+
create_tiledb_frame_from_dataframe(f"{args['output_dir']}/sample_metadata", sample_metadata)
|
|
64
|
+
|
|
65
|
+
# Save metadata for subsequent steps
|
|
66
|
+
metadata = {"num_samples": len(sample_names), "total_cells": int(sample_metadata["cellarr_cell_counts"].sum())}
|
|
67
|
+
|
|
68
|
+
Path(args["temp_dir"]).mkdir(parents=True, exist_ok=True)
|
|
69
|
+
with open(f"{args['temp_dir']}/sample_metadata.json", "w") as f:
|
|
70
|
+
json.dump(metadata, f)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
if len(sys.argv) != 2:
|
|
75
|
+
print("Usage: python process_sample_metadata.py '<json_args>'")
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
|
|
78
|
+
process_sample_metadata(sys.argv[1])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cellarr
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1.dev2
|
|
4
4
|
Summary: TileDB-based array storage for genomics data collections.
|
|
5
5
|
Home-page: https://github.com/BiocPy/cellarr
|
|
6
6
|
Author: Jayaram Kancherla
|
|
@@ -45,7 +45,7 @@ datasets but can be generalized to store any 2-dimensional experimental data.
|
|
|
45
45
|
|
|
46
46
|
To get started, install the package from [PyPI](https://pypi.org/project/cellarr/)
|
|
47
47
|
|
|
48
|
-
```
|
|
48
|
+
```sh
|
|
49
49
|
pip install cellarr
|
|
50
50
|
|
|
51
51
|
## to include optional dependencies
|
|
@@ -156,6 +156,47 @@ print(dataset)
|
|
|
156
156
|
|
|
157
157
|
Check out the [documentation](https://biocpy.github.io/cellarr/tutorial.html) for more details.
|
|
158
158
|
|
|
159
|
+
### Building on HPC environments with `slurm`
|
|
160
|
+
|
|
161
|
+
To simplify building TileDB files on HPC environments that use `slurm`, there are a few steps you need to follow.
|
|
162
|
+
|
|
163
|
+
- Step 1: Construct a manifest file
|
|
164
|
+
A minimal manifest file (json) must contain the following fields
|
|
165
|
+
- `"files"`: A list of file path to the input `h5ad` objects.
|
|
166
|
+
- `"python_env"`: A set of commands to activate the Python environment containing this package and its dependencies.
|
|
167
|
+
|
|
168
|
+
Here’s an example of the manifest file:
|
|
169
|
+
```json
|
|
170
|
+
manifest = {
|
|
171
|
+
"files": your/list/of/files,
|
|
172
|
+
"python_env": """
|
|
173
|
+
ml Miniforge3
|
|
174
|
+
conda activate cellarr
|
|
175
|
+
|
|
176
|
+
python --version
|
|
177
|
+
which python
|
|
178
|
+
""",
|
|
179
|
+
"matrix_options": [
|
|
180
|
+
{
|
|
181
|
+
"matrix_name": "non_zero_cells",
|
|
182
|
+
"dtype": "uint32"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"matrix_name": "pseudo_bulk_log_normed",
|
|
186
|
+
"dtype": "float32"
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
For more options, check out the [README](./src/cellarr/slurm/README.md).
|
|
192
|
+
|
|
193
|
+
- Step 2: Submit the job
|
|
194
|
+
Once your manifest file is ready, you can submit the necessary jobs using the `cellarr_build` CLI. Run the following command:
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
cellarr_build --input-manifest your/path/to/test_manifest.json --output-dir your/path/to/output --memory-per-job 8 --cpus-per-task 2
|
|
198
|
+
```
|
|
199
|
+
|
|
159
200
|
### Query a `CellArrDataset`
|
|
160
201
|
|
|
161
202
|
Users have the option to reuse the `dataset` object returned when building the dataset or by creating a `CellArrDataset` object by initializing it to the path where the files were created.
|
|
@@ -42,9 +42,20 @@ src/cellarr/utils_anndata.py
|
|
|
42
42
|
src/cellarr.egg-info/PKG-INFO
|
|
43
43
|
src/cellarr.egg-info/SOURCES.txt
|
|
44
44
|
src/cellarr.egg-info/dependency_links.txt
|
|
45
|
+
src/cellarr.egg-info/entry_points.txt
|
|
45
46
|
src/cellarr.egg-info/not-zip-safe
|
|
46
47
|
src/cellarr.egg-info/requires.txt
|
|
47
48
|
src/cellarr.egg-info/top_level.txt
|
|
49
|
+
src/cellarr/slurm/README.md
|
|
50
|
+
src/cellarr/slurm/__init__.py
|
|
51
|
+
src/cellarr/slurm/build_cellarr_steps.py
|
|
52
|
+
src/cellarr/slurm/final_assembly.py
|
|
53
|
+
src/cellarr/slurm/finalize_matrix.py
|
|
54
|
+
src/cellarr/slurm/process_cell_metadata.py
|
|
55
|
+
src/cellarr/slurm/process_gene_annotation.py
|
|
56
|
+
src/cellarr/slurm/process_matrix.py
|
|
57
|
+
src/cellarr/slurm/process_matrix_all.py
|
|
58
|
+
src/cellarr/slurm/process_sample_metadata.py
|
|
48
59
|
tests/conftest.py
|
|
49
60
|
tests/test_anndata_utils.py
|
|
50
61
|
tests/test_build.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|