cellarr 0.3.2__tar.gz → 0.4.1.dev1__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.3.2 → cellarr-0.4.1.dev1}/.github/workflows/pypi-publish.yml +2 -2
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/.github/workflows/pypi-test.yml +1 -1
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/.pre-commit-config.yaml +2 -2
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/CHANGELOG.md +5 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/PKG-INFO +2 -2
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/setup.cfg +1 -1
- cellarr-0.4.1.dev1/src/cellarr/slurm/README.md +58 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/__init__.py +0 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/build_cellarr_steps.py +287 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/build_cellarr_steps_bkp.py +185 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/final_assembly.py +20 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/finalize_matrix.py +45 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/process_cell_metadata.py +68 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/process_gene_annotation.py +33 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/process_matrix.py +50 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/process_matrix_all.py +40 -0
- cellarr-0.4.1.dev1/src/cellarr/slurm/process_sample_metadata.py +76 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr.egg-info/PKG-INFO +2 -2
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr.egg-info/SOURCES.txt +11 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/.coveragerc +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/.gitignore +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/.readthedocs.yml +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/AUTHORS.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/CONTRIBUTING.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/LICENSE.txt +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/README.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/assets/cellarr.jpg +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/assets/cellarr.png +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/assets/cellarr.svg +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/Makefile +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/_static/.gitignore +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/authors.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/changelog.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/conf.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/contributing.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/index.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/license.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/notes.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/readme.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/requirements.txt +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/docs/tutorial.md +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/pyproject.toml +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/setup.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/CellArrDataset.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/CellArrDatasetSlice.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/__init__.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/autoencoder.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/build_cellarrdataset.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/build_options.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/buildutils_tiledb_array.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/buildutils_tiledb_frame.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/dataloader.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/queryutils_tiledb_frame.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr/utils_anndata.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr.egg-info/dependency_links.txt +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr.egg-info/not-zip-safe +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr.egg-info/requires.txt +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/src/cellarr.egg-info/top_level.txt +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/tests/conftest.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/tests/data/adata.h5ad +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/tests/test_anndata_utils.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/tests/test_build.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/tests/test_custom_config.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/tests/test_dataloader.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/tests/test_query.py +0 -0
- {cellarr-0.3.2 → cellarr-0.4.1.dev1}/tox.ini +0 -0
|
@@ -14,10 +14,10 @@ jobs:
|
|
|
14
14
|
|
|
15
15
|
steps:
|
|
16
16
|
- uses: actions/checkout@v4
|
|
17
|
-
- name: Set up Python 3.
|
|
17
|
+
- name: Set up Python 3.11
|
|
18
18
|
uses: actions/setup-python@v5
|
|
19
19
|
with:
|
|
20
|
-
python-version: 3.
|
|
20
|
+
python-version: 3.11
|
|
21
21
|
- name: Install dependencies
|
|
22
22
|
run: |
|
|
23
23
|
python -m pip install --upgrade pip
|
|
@@ -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.
|
|
3
|
+
Version: 0.4.1.dev1
|
|
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
|
|
@@ -10,7 +10,7 @@ Project-URL: Documentation, https://github.com/BiocPy/cellarr
|
|
|
10
10
|
Platform: any
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: Programming Language :: Python
|
|
13
|
-
Requires-Python: >=3.
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
14
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
15
15
|
License-File: LICENSE.txt
|
|
16
16
|
Requires-Dist: importlib-metadata; python_version < "3.8"
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
"metadata": {
|
|
41
|
+
"processing_date": ["2024-01-01", "2024-01-02", ...]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Run
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
|
|
52
|
+
python build_cellarr_steps.py \
|
|
53
|
+
--input-manifest manifest.json \
|
|
54
|
+
--output-dir /path/to/output \
|
|
55
|
+
--memory-per-job 64 \
|
|
56
|
+
--cpus-per-task 4
|
|
57
|
+
|
|
58
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,287 @@
|
|
|
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 anndata
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from cellarr.buildutils_tiledb_array import create_tiledb_array
|
|
11
|
+
|
|
12
|
+
__author__ = "Jayaram Kancherla"
|
|
13
|
+
__copyright__ = "Jayaram Kancherla"
|
|
14
|
+
__license__ = "MIT"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SlurmBuilder:
|
|
18
|
+
"""SLURM-based builder for CellArrDataset."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
output_dir: str,
|
|
23
|
+
log_dir: str,
|
|
24
|
+
temp_dir: str,
|
|
25
|
+
memory_gb: int = 64,
|
|
26
|
+
time_hours: int = 24,
|
|
27
|
+
cpus_per_task: int = 4,
|
|
28
|
+
):
|
|
29
|
+
"""Initialize the SLURM builder.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
output_dir:
|
|
33
|
+
Path to final output directory.
|
|
34
|
+
|
|
35
|
+
log_dir:
|
|
36
|
+
Path to store SLURM logs.
|
|
37
|
+
|
|
38
|
+
temp_dir:
|
|
39
|
+
Path for temporary files.
|
|
40
|
+
|
|
41
|
+
memory_gb:
|
|
42
|
+
Memory per job in GB.
|
|
43
|
+
|
|
44
|
+
time_hours:
|
|
45
|
+
Time limit per job in hours.
|
|
46
|
+
|
|
47
|
+
cpus_per_task:
|
|
48
|
+
CPUs per task.
|
|
49
|
+
"""
|
|
50
|
+
self.output_dir = Path(output_dir)
|
|
51
|
+
self.log_dir = Path(log_dir)
|
|
52
|
+
self.temp_dir = Path(temp_dir)
|
|
53
|
+
self.memory_gb = memory_gb
|
|
54
|
+
self.time_hours = time_hours
|
|
55
|
+
self.cpus_per_task = cpus_per_task
|
|
56
|
+
|
|
57
|
+
def create_slurm_script(
|
|
58
|
+
self, job_name: str, python_script: str, args: Dict, dependencies: Optional[str] = None
|
|
59
|
+
) -> str:
|
|
60
|
+
"""Create a SLURM job submission script."""
|
|
61
|
+
script = f"""#!/bin/bash
|
|
62
|
+
#SBATCH --job-name={job_name}
|
|
63
|
+
#SBATCH --output={self.log_dir}/{job_name}_%j.out
|
|
64
|
+
#SBATCH --error={self.log_dir}/{job_name}_%j.err
|
|
65
|
+
#SBATCH --time={self.time_hours}:00:00
|
|
66
|
+
#SBATCH --mem={self.memory_gb}G
|
|
67
|
+
#SBATCH --cpus-per-task={self.cpus_per_task}
|
|
68
|
+
"""
|
|
69
|
+
if dependencies:
|
|
70
|
+
script += f"#SBATCH --dependency={dependencies}\n"
|
|
71
|
+
|
|
72
|
+
script += f"""
|
|
73
|
+
source ~/.bashrc
|
|
74
|
+
conda activate cellarr_env # MODIFY AS NEEDED
|
|
75
|
+
|
|
76
|
+
python {python_script} '{json.dumps(args)}'
|
|
77
|
+
"""
|
|
78
|
+
script_path = self.log_dir / f"{job_name}_submit.sh"
|
|
79
|
+
with open(script_path, "w") as f:
|
|
80
|
+
f.write(script)
|
|
81
|
+
return script_path
|
|
82
|
+
|
|
83
|
+
def create_array_script(
|
|
84
|
+
self, job_name: str, python_script: str, args: Dict, n_tasks: int, dependencies: Optional[str] = None
|
|
85
|
+
) -> str:
|
|
86
|
+
"""Create a SLURM array job submission script."""
|
|
87
|
+
script = f"""#!/bin/bash
|
|
88
|
+
#SBATCH --job-name={job_name}
|
|
89
|
+
#SBATCH --output={self.log_dir}/{job_name}_%A_%a.out
|
|
90
|
+
#SBATCH --error={self.log_dir}/{job_name}_%A_%a.err
|
|
91
|
+
#SBATCH --time={self.time_hours}:00:00
|
|
92
|
+
#SBATCH --mem={self.memory_gb}G
|
|
93
|
+
#SBATCH --cpus-per-task={self.cpus_per_task}
|
|
94
|
+
#SBATCH --array=0-{n_tasks-1}
|
|
95
|
+
"""
|
|
96
|
+
if dependencies:
|
|
97
|
+
script += f"#SBATCH --dependency={dependencies}\n"
|
|
98
|
+
|
|
99
|
+
script += f"""
|
|
100
|
+
source ~/.bashrc
|
|
101
|
+
conda activate cellarr_env # MODIFY AS NEEDED
|
|
102
|
+
|
|
103
|
+
python {python_script} '{json.dumps(args)}'
|
|
104
|
+
"""
|
|
105
|
+
script_path = self.log_dir / f"{job_name}_array.sh"
|
|
106
|
+
with open(script_path, "w") as f:
|
|
107
|
+
f.write(script)
|
|
108
|
+
return script_path
|
|
109
|
+
|
|
110
|
+
def submit_job(self, script_path: str) -> str:
|
|
111
|
+
"""Submit a SLURM job and return job ID."""
|
|
112
|
+
result = subprocess.run(["sbatch", script_path], capture_output=True, text=True)
|
|
113
|
+
return result.stdout.strip().split()[-1]
|
|
114
|
+
|
|
115
|
+
def submit_gene_annotation_job(self, files: List[str], gene_options: Dict) -> str:
|
|
116
|
+
"""Submit gene annotation processing job."""
|
|
117
|
+
args = {
|
|
118
|
+
"files": files,
|
|
119
|
+
"output_dir": str(self.output_dir),
|
|
120
|
+
"gene_options": gene_options,
|
|
121
|
+
"temp_dir": str(self.temp_dir / "gene_annotation"),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
script_path = self.create_slurm_script(
|
|
125
|
+
job_name="cellarr_gene_annot", python_script="process_gene_annotation.py", args=args
|
|
126
|
+
)
|
|
127
|
+
return self.submit_job(script_path)
|
|
128
|
+
|
|
129
|
+
def submit_sample_metadata_job(self, files: List[str], sample_options: Dict, dependency: str) -> str:
|
|
130
|
+
"""Submit sample metadata processing job."""
|
|
131
|
+
args = {
|
|
132
|
+
"files": files,
|
|
133
|
+
"output_dir": str(self.output_dir),
|
|
134
|
+
"sample_options": sample_options,
|
|
135
|
+
"temp_dir": str(self.temp_dir / "sample_metadata"),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
script_path = self.create_slurm_script(
|
|
139
|
+
job_name="cellarr_sample_meta",
|
|
140
|
+
python_script="process_sample_metadata.py",
|
|
141
|
+
args=args,
|
|
142
|
+
# dependencies=f"afterok:{dependency}",
|
|
143
|
+
)
|
|
144
|
+
return self.submit_job(script_path)
|
|
145
|
+
|
|
146
|
+
def submit_cell_metadata_job(self, files: List[str], cell_options: Dict, dependency: str) -> str:
|
|
147
|
+
"""Submit cell metadata processing job."""
|
|
148
|
+
args = {
|
|
149
|
+
"files": files,
|
|
150
|
+
"output_dir": str(self.output_dir),
|
|
151
|
+
"cell_options": cell_options,
|
|
152
|
+
"temp_dir": str(self.temp_dir / "cell_metadata"),
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
script_path = self.create_slurm_script(
|
|
156
|
+
job_name="cellarr_cell_meta",
|
|
157
|
+
python_script="process_cell_metadata.py",
|
|
158
|
+
args=args,
|
|
159
|
+
# dependencies=f"afterok:{dependency}",
|
|
160
|
+
)
|
|
161
|
+
return self.submit_job(script_path)
|
|
162
|
+
|
|
163
|
+
def submit_matrix_processing(self, files: List[str], matrix_options: Dict, dependency: str) -> Tuple[str, str]:
|
|
164
|
+
"""Submit matrix processing as SLURM array job."""
|
|
165
|
+
# Calculate cell offsets for each file
|
|
166
|
+
offset = 0
|
|
167
|
+
file_infos = []
|
|
168
|
+
for file in files:
|
|
169
|
+
file_infos.append({"file": file, "offset": offset})
|
|
170
|
+
adata = anndata.read_h5ad(file, backed="r")
|
|
171
|
+
offset += adata.n_obs
|
|
172
|
+
|
|
173
|
+
# Create matrix TileDB array
|
|
174
|
+
matrix_uri = str(self.output_dir / "assays" / matrix_options["matrix_name"])
|
|
175
|
+
create_tiledb_array(
|
|
176
|
+
matrix_uri,
|
|
177
|
+
matrix_attr_name=matrix_options.get("matrix_attr_name", "data"),
|
|
178
|
+
matrix_dim_dtype=np.dtype(matrix_options.get("dtype", "float32")),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Prepare array job arguments
|
|
182
|
+
array_args = {
|
|
183
|
+
"output_dir": str(self.output_dir),
|
|
184
|
+
"temp_dir": str(self.temp_dir / f"matrix_{matrix_options['matrix_name']}"),
|
|
185
|
+
"matrix_options": matrix_options,
|
|
186
|
+
"gene_annotation_file": str(self.temp_dir / "gene_annotation/gene_set.json"),
|
|
187
|
+
"file_infos": file_infos,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# Submit array job
|
|
191
|
+
array_script = self.create_array_script(
|
|
192
|
+
job_name=f"matrix_{matrix_options['matrix_name']}",
|
|
193
|
+
python_script="process_matrix.py",
|
|
194
|
+
args=array_args,
|
|
195
|
+
n_tasks=len(files),
|
|
196
|
+
dependencies=f"afterok:{dependency}",
|
|
197
|
+
)
|
|
198
|
+
array_job_id = self.submit_job(array_script)
|
|
199
|
+
|
|
200
|
+
# Submit finalization job
|
|
201
|
+
final_args = {
|
|
202
|
+
"output_dir": str(self.output_dir),
|
|
203
|
+
"temp_dir": str(self.temp_dir / f"matrix_{matrix_options['matrix_name']}"),
|
|
204
|
+
"matrix_options": matrix_options,
|
|
205
|
+
"file_infos": file_infos,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
final_script = self.create_slurm_script(
|
|
209
|
+
job_name=f"finalize_matrix_{matrix_options['matrix_name']}",
|
|
210
|
+
python_script="finalize_matrix.py",
|
|
211
|
+
args=final_args,
|
|
212
|
+
dependencies=f"afterok:{array_job_id}",
|
|
213
|
+
)
|
|
214
|
+
final_job_id = self.submit_job(final_script)
|
|
215
|
+
|
|
216
|
+
return array_job_id, final_job_id
|
|
217
|
+
|
|
218
|
+
def submit_final_assembly(self, matrix_names: List[str], dependencies: List[str]) -> str:
|
|
219
|
+
"""Submit final assembly job."""
|
|
220
|
+
args = {"input_dir": str(self.output_dir), "output_dir": str(self.output_dir), "matrix_names": matrix_names}
|
|
221
|
+
|
|
222
|
+
script_path = self.create_slurm_script(
|
|
223
|
+
job_name="cellarr_final_assembly",
|
|
224
|
+
python_script="final_assembly.py",
|
|
225
|
+
args=args,
|
|
226
|
+
dependencies=f"afterok:{','.join(dependencies)}",
|
|
227
|
+
)
|
|
228
|
+
return self.submit_job(script_path)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def main():
|
|
232
|
+
parser = argparse.ArgumentParser(description="Build CellArrDataset using SLURM steps")
|
|
233
|
+
parser.add_argument("--input-manifest", required=True, help="Path to JSON manifest file")
|
|
234
|
+
parser.add_argument("--output-dir", required=True, help="Base output directory")
|
|
235
|
+
parser.add_argument("--memory-per-job", type=int, default=64, help="Memory in GB per job")
|
|
236
|
+
parser.add_argument("--time-per-job", type=int, default=24, help="Time in hours per job")
|
|
237
|
+
parser.add_argument("--cpus-per-task", type=int, default=4, help="CPUs per task")
|
|
238
|
+
args = parser.parse_args()
|
|
239
|
+
|
|
240
|
+
# Create directories
|
|
241
|
+
base_dir = Path(args.output_dir)
|
|
242
|
+
log_dir = base_dir / "logs"
|
|
243
|
+
temp_dir = base_dir / "temp"
|
|
244
|
+
final_dir = base_dir / "final"
|
|
245
|
+
|
|
246
|
+
for d in [log_dir, temp_dir, final_dir]:
|
|
247
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
248
|
+
|
|
249
|
+
# Read manifest
|
|
250
|
+
with open(args.input_manifest) as f:
|
|
251
|
+
manifest = json.load(f)
|
|
252
|
+
|
|
253
|
+
# Initialize builder
|
|
254
|
+
builder = SlurmBuilder(
|
|
255
|
+
output_dir=str(final_dir),
|
|
256
|
+
log_dir=str(log_dir),
|
|
257
|
+
temp_dir=str(temp_dir),
|
|
258
|
+
memory_gb=args.memory_per_job,
|
|
259
|
+
time_hours=args.time_per_job,
|
|
260
|
+
cpus_per_task=args.cpus_per_task,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Submit jobs
|
|
264
|
+
gene_job_id = builder.submit_gene_annotation_job(manifest["files"], manifest.get("gene_options", {}))
|
|
265
|
+
|
|
266
|
+
sample_job_id = builder.submit_sample_metadata_job(
|
|
267
|
+
manifest["files"], manifest.get("sample_options", {}), gene_job_id
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
cell_job_id = builder.submit_cell_metadata_job(manifest["files"], manifest.get("cell_options", {}), sample_job_id)
|
|
271
|
+
|
|
272
|
+
# Process matrices
|
|
273
|
+
matrix_options = manifest.get("matrix_options", [{"matrix_name": "counts"}])
|
|
274
|
+
if not isinstance(matrix_options, list):
|
|
275
|
+
matrix_options = [matrix_options]
|
|
276
|
+
|
|
277
|
+
matrix_job_ids = []
|
|
278
|
+
for matrix_opt in matrix_options:
|
|
279
|
+
_, final_id = builder.submit_matrix_processing(manifest["files"], matrix_opt, f"{cell_job_id},{gene_job_id}")
|
|
280
|
+
matrix_job_ids.append(final_id)
|
|
281
|
+
|
|
282
|
+
# Submit final assembly
|
|
283
|
+
builder.submit_final_assembly([opt["matrix_name"] for opt in matrix_options], matrix_job_ids)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
if __name__ == "__main__":
|
|
287
|
+
main()
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__author__ = "Jayaram Kancherla"
|
|
10
|
+
__copyright__ = "Jayaram Kancherla"
|
|
11
|
+
__license__ = "MIT"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_slurm_script(
|
|
15
|
+
job_name: str,
|
|
16
|
+
log_dir: str,
|
|
17
|
+
python_script: str,
|
|
18
|
+
args: Dict,
|
|
19
|
+
memory_gb: int = 64,
|
|
20
|
+
time_hours: int = 24,
|
|
21
|
+
cpus_per_task: int = 4,
|
|
22
|
+
dependencies: Optional[str] = None,
|
|
23
|
+
) -> str:
|
|
24
|
+
"""Create SLURM job submission script."""
|
|
25
|
+
script = f"""#!/bin/bash
|
|
26
|
+
#SBATCH --job-name={job_name}
|
|
27
|
+
#SBATCH --output={log_dir}/{job_name}_%j.out
|
|
28
|
+
#SBATCH --error={log_dir}/{job_name}_%j.err
|
|
29
|
+
#SBATCH --time={time_hours}:00:00
|
|
30
|
+
#SBATCH --mem={memory_gb}G
|
|
31
|
+
#SBATCH --cpus-per-task={cpus_per_task}
|
|
32
|
+
"""
|
|
33
|
+
if dependencies:
|
|
34
|
+
script += f"#SBATCH --dependency={dependencies}\n"
|
|
35
|
+
|
|
36
|
+
script += """
|
|
37
|
+
source ~/.bashrc
|
|
38
|
+
conda activate cellarr_env # Modify as needed for your environment
|
|
39
|
+
|
|
40
|
+
python {python_script} '{json.dumps(args)}'
|
|
41
|
+
"""
|
|
42
|
+
script_path = os.path.join(log_dir, f"{job_name}_submit.sh")
|
|
43
|
+
with open(script_path, "w") as f:
|
|
44
|
+
f.write(script)
|
|
45
|
+
return script_path
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def submit_slurm_job(script_path: str, dependency: Optional[str] = None) -> str:
|
|
49
|
+
"""Submit SLURM job and return job ID."""
|
|
50
|
+
cmd = ["sbatch"]
|
|
51
|
+
if dependency:
|
|
52
|
+
cmd.extend(["--dependency", dependency])
|
|
53
|
+
cmd.append(script_path)
|
|
54
|
+
|
|
55
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
56
|
+
job_id = result.stdout.strip().split()[-1]
|
|
57
|
+
return job_id
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main():
|
|
61
|
+
parser = argparse.ArgumentParser(description="Build CellArrDataset using SLURM steps")
|
|
62
|
+
parser.add_argument("--input-manifest", required=True, help="Path to JSON manifest file")
|
|
63
|
+
parser.add_argument("--output-dir", required=True, help="Base output directory")
|
|
64
|
+
parser.add_argument("--memory-per-job", type=int, default=64, help="Memory in GB per job")
|
|
65
|
+
parser.add_argument("--time-per-job", type=int, default=24, help="Time in hours per job")
|
|
66
|
+
parser.add_argument("--cpus-per-task", type=int, default=4, help="CPUs per task")
|
|
67
|
+
args = parser.parse_args()
|
|
68
|
+
|
|
69
|
+
# Create output directories
|
|
70
|
+
base_dir = Path(args.output_dir)
|
|
71
|
+
log_dir = base_dir / "logs"
|
|
72
|
+
temp_dir = base_dir / "temp"
|
|
73
|
+
final_dir = base_dir / "final"
|
|
74
|
+
|
|
75
|
+
for d in [log_dir, temp_dir, final_dir]:
|
|
76
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
77
|
+
|
|
78
|
+
# Read manifest file
|
|
79
|
+
with open(args.input_manifest) as f:
|
|
80
|
+
manifest = json.load(f)
|
|
81
|
+
|
|
82
|
+
# Step 1: Gene Annotation Job
|
|
83
|
+
gene_args = {
|
|
84
|
+
"files": manifest["files"],
|
|
85
|
+
"output_dir": str(final_dir),
|
|
86
|
+
"gene_options": manifest.get("gene_options", {}),
|
|
87
|
+
"temp_dir": str(temp_dir / "gene_annotation"),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
gene_script = create_slurm_script(
|
|
91
|
+
job_name="cellarr_gene_annot",
|
|
92
|
+
log_dir=str(log_dir),
|
|
93
|
+
python_script="process_gene_annotation.py",
|
|
94
|
+
args=gene_args,
|
|
95
|
+
memory_gb=args.memory_per_job,
|
|
96
|
+
cpus_per_task=args.cpus_per_task,
|
|
97
|
+
)
|
|
98
|
+
gene_job_id = submit_slurm_job(gene_script)
|
|
99
|
+
|
|
100
|
+
# Step 2: Sample Metadata Job
|
|
101
|
+
sample_args = {
|
|
102
|
+
"files": manifest["files"],
|
|
103
|
+
"output_dir": str(final_dir),
|
|
104
|
+
"sample_options": manifest.get("sample_options", {}),
|
|
105
|
+
"temp_dir": str(temp_dir / "sample_metadata"),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
sample_script = create_slurm_script(
|
|
109
|
+
job_name="cellarr_sample_meta",
|
|
110
|
+
log_dir=str(log_dir),
|
|
111
|
+
python_script="process_sample_metadata.py",
|
|
112
|
+
args=sample_args,
|
|
113
|
+
memory_gb=args.memory_per_job,
|
|
114
|
+
cpus_per_task=args.cpus_per_task,
|
|
115
|
+
# dependencies=f"afterok:{gene_job_id}",
|
|
116
|
+
)
|
|
117
|
+
sample_job_id = submit_slurm_job(sample_script)
|
|
118
|
+
|
|
119
|
+
# Step 3: Cell Metadata Job
|
|
120
|
+
cell_args = {
|
|
121
|
+
"files": manifest["files"],
|
|
122
|
+
"output_dir": str(final_dir),
|
|
123
|
+
"cell_options": manifest.get("cell_options", {}),
|
|
124
|
+
"temp_dir": str(temp_dir / "cell_metadata"),
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
cell_script = create_slurm_script(
|
|
128
|
+
job_name="cellarr_cell_meta",
|
|
129
|
+
log_dir=str(log_dir),
|
|
130
|
+
python_script="process_cell_metadata.py",
|
|
131
|
+
args=cell_args,
|
|
132
|
+
memory_gb=args.memory_per_job,
|
|
133
|
+
cpus_per_task=args.cpus_per_task,
|
|
134
|
+
# dependencies=f"afterok:{sample_job_id}",
|
|
135
|
+
)
|
|
136
|
+
cell_job_id = submit_slurm_job(cell_script)
|
|
137
|
+
|
|
138
|
+
# Step 4: Matrix Jobs (one per matrix type)
|
|
139
|
+
matrix_job_ids = []
|
|
140
|
+
matrix_options = manifest.get("matrix_options", [{"matrix_name": "counts"}])
|
|
141
|
+
if not isinstance(matrix_options, list):
|
|
142
|
+
matrix_options = [matrix_options]
|
|
143
|
+
|
|
144
|
+
for matrix_opt in matrix_options:
|
|
145
|
+
matrix_args = {
|
|
146
|
+
"files": manifest["files"],
|
|
147
|
+
"output_dir": str(final_dir),
|
|
148
|
+
"matrix_options": matrix_opt,
|
|
149
|
+
"gene_annotation_file": str(temp_dir / "gene_annotation/gene_set.json"),
|
|
150
|
+
"temp_dir": str(temp_dir / f"matrix_{matrix_opt['matrix_name']}"),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
matrix_script = create_slurm_script(
|
|
154
|
+
job_name=f"cellarr_matrix_{matrix_opt['matrix_name']}",
|
|
155
|
+
log_dir=str(log_dir),
|
|
156
|
+
python_script="process_matrix.py",
|
|
157
|
+
args=matrix_args,
|
|
158
|
+
memory_gb=args.memory_per_job * 2, # More memory for matrix processing
|
|
159
|
+
cpus_per_task=args.cpus_per_task,
|
|
160
|
+
dependencies=f"afterok:{cell_job_id},{gene_job_id}",
|
|
161
|
+
)
|
|
162
|
+
job_id = submit_slurm_job(matrix_script)
|
|
163
|
+
matrix_job_ids.append(job_id)
|
|
164
|
+
|
|
165
|
+
# Step 5: Final Assembly Job
|
|
166
|
+
final_args = {
|
|
167
|
+
"input_dir": str(final_dir),
|
|
168
|
+
"output_dir": str(final_dir),
|
|
169
|
+
"matrix_names": [opt["matrix_name"] for opt in matrix_options],
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
final_script = create_slurm_script(
|
|
173
|
+
job_name="cellarr_final_assembly",
|
|
174
|
+
log_dir=str(log_dir),
|
|
175
|
+
python_script="final_assembly.py",
|
|
176
|
+
args=final_args,
|
|
177
|
+
memory_gb=args.memory_per_job,
|
|
178
|
+
cpus_per_task=args.cpus_per_task,
|
|
179
|
+
dependencies=f"afterok:{','.join(matrix_job_ids)}",
|
|
180
|
+
)
|
|
181
|
+
submit_slurm_job(final_script)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
main()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from cellarr import CellArrDataset
|
|
4
|
+
|
|
5
|
+
__author__ = "Jayaram Kancherla"
|
|
6
|
+
__copyright__ = "Jayaram Kancherla"
|
|
7
|
+
__license__ = "MIT"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def final_assembly(args_json: str):
|
|
11
|
+
args = json.loads(args_json)
|
|
12
|
+
|
|
13
|
+
# Perform any final optimizations or validations
|
|
14
|
+
dataset = CellArrDataset(dataset_path=args["output_dir"], assay_uri=args["matrix_names"])
|
|
15
|
+
|
|
16
|
+
# Save final metadata
|
|
17
|
+
metadata = {"shape": dataset.shape, "matrices": args["matrix_names"]}
|
|
18
|
+
|
|
19
|
+
with open(f"{args['output_dir']}/metadata.json", "w") as f:
|
|
20
|
+
json.dump(metadata, f)
|
|
@@ -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["file_infos"])
|
|
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["file_infos"]),
|
|
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,33 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from cellarr import utils_anndata as uad
|
|
6
|
+
from cellarr.buildutils_tiledb_frame import create_tiledb_frame_from_dataframe
|
|
7
|
+
|
|
8
|
+
__author__ = "Jayaram Kancherla"
|
|
9
|
+
__copyright__ = "Jayaram Kancherla"
|
|
10
|
+
__license__ = "MIT"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def process_gene_annotation(args_json: str):
|
|
14
|
+
args = json.loads(args_json)
|
|
15
|
+
|
|
16
|
+
# Extract gene information from all files
|
|
17
|
+
files_cache = uad.extract_anndata_info(
|
|
18
|
+
args["files"], var_feature_column=args["gene_options"].get("feature_column", "index")
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Scan for features
|
|
22
|
+
gene_set = uad.scan_for_features(files_cache)
|
|
23
|
+
gene_set = sorted(gene_set)
|
|
24
|
+
|
|
25
|
+
# Create gene annotation dataframe
|
|
26
|
+
gene_annotation = pd.DataFrame({"cellarr_gene_index": gene_set}, index=gene_set)
|
|
27
|
+
|
|
28
|
+
# Save gene set for later use
|
|
29
|
+
with open(f"{args['temp_dir']}/gene_set.json", "w") as f:
|
|
30
|
+
json.dump(gene_set, f)
|
|
31
|
+
|
|
32
|
+
# Create TileDB store
|
|
33
|
+
create_tiledb_frame_from_dataframe(f"{args['output_dir']}/gene_annotation", gene_annotation)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from cellarr import utils_anndata as uad
|
|
7
|
+
from cellarr.buildutils_tiledb_array import write_csr_matrix_to_tiledb
|
|
8
|
+
|
|
9
|
+
__author__ = "Jayaram Kancherla"
|
|
10
|
+
__copyright__ = "Jayaram Kancherla"
|
|
11
|
+
__license__ = "MIT"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def process_matrix_file(args_json: str):
|
|
15
|
+
"""Process a single file for matrix creation."""
|
|
16
|
+
args = json.loads(args_json)
|
|
17
|
+
|
|
18
|
+
# Get SLURM array task ID
|
|
19
|
+
task_id = int(os.environ.get("SLURM_ARRAY_TASK_ID", 0))
|
|
20
|
+
|
|
21
|
+
# Load gene set mapping
|
|
22
|
+
with open(args["gene_annotation_file"]) as f:
|
|
23
|
+
gene_set = json.load(f)
|
|
24
|
+
gene_map = {gene: idx for idx, gene in enumerate(gene_set)}
|
|
25
|
+
|
|
26
|
+
# Get file and offset for this task
|
|
27
|
+
file_info = args["file_infos"][task_id]
|
|
28
|
+
input_file = file_info["file"]
|
|
29
|
+
row_offset = file_info["offset"]
|
|
30
|
+
|
|
31
|
+
# Process the file
|
|
32
|
+
matrix = uad.remap_anndata(input_file, gene_map, layer_matrix_name=args["matrix_options"]["matrix_name"])
|
|
33
|
+
|
|
34
|
+
# Write to TileDB
|
|
35
|
+
matrix_uri = f"{args['output_dir']}/assays/{args['matrix_options']['matrix_name']}"
|
|
36
|
+
write_csr_matrix_to_tiledb(matrix_uri, matrix[args["matrix_options"]["matrix_name"]], row_offset=row_offset)
|
|
37
|
+
|
|
38
|
+
# Save task completion marker
|
|
39
|
+
task_marker = Path(args["temp_dir"]) / "completed" / f"task_{task_id}.json"
|
|
40
|
+
task_marker.parent.mkdir(exist_ok=True)
|
|
41
|
+
with open(task_marker, "w") as f:
|
|
42
|
+
json.dump({"file": input_file, "cells_processed": matrix[args["matrix_options"]["matrix_name"]].shape[0]}, f)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
if len(sys.argv) != 2:
|
|
47
|
+
print("Usage: python process_matrix.py '<json_args>'")
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
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,76 @@
|
|
|
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 create_tiledb_frame_from_dataframe
|
|
8
|
+
|
|
9
|
+
__author__ = "Jayaram Kancherla"
|
|
10
|
+
__copyright__ = "Jayaram Kancherla"
|
|
11
|
+
__license__ = "MIT"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def process_sample_metadata(args_json: str):
|
|
15
|
+
"""Process and create sample metadata store.
|
|
16
|
+
|
|
17
|
+
Creates sample metadata including:
|
|
18
|
+
- Basic sample information
|
|
19
|
+
- Cell counts per sample
|
|
20
|
+
- Original gene sets
|
|
21
|
+
- Sample index information
|
|
22
|
+
"""
|
|
23
|
+
args = json.loads(args_json)
|
|
24
|
+
|
|
25
|
+
# Extract information from files
|
|
26
|
+
files_cache = uad.extract_anndata_info(args["files"], var_feature_column=args.get("feature_column", "index"))
|
|
27
|
+
|
|
28
|
+
# Get cell counts for each sample
|
|
29
|
+
cell_counts = uad.scan_for_cellcounts(files_cache)
|
|
30
|
+
|
|
31
|
+
# Create basic sample metadata
|
|
32
|
+
sample_names = [f"sample_{idx+1}" for idx in range(len(args["files"]))]
|
|
33
|
+
sample_metadata = pd.DataFrame(
|
|
34
|
+
{"cellarr_sample": sample_names, "cellarr_cell_counts": cell_counts, "cellarr_filename": args["files"]}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Add sample indices for efficient slicing
|
|
38
|
+
counter = sample_metadata["cellarr_cell_counts"].shift(1)
|
|
39
|
+
counter.iloc[0] = 0
|
|
40
|
+
sample_metadata["cellarr_sample_start_index"] = counter.cumsum().astype(int)
|
|
41
|
+
|
|
42
|
+
# Calculate end indices
|
|
43
|
+
ends = sample_metadata["cellarr_sample_start_index"].shift(-1)
|
|
44
|
+
ends.iloc[-1] = int(sample_metadata["cellarr_cell_counts"].sum())
|
|
45
|
+
ends = ends - 1
|
|
46
|
+
sample_metadata["cellarr_sample_end_index"] = ends.astype(int)
|
|
47
|
+
|
|
48
|
+
# Add original gene sets for each sample
|
|
49
|
+
gene_sets = uad.scan_for_features(files_cache, unique=False)
|
|
50
|
+
sample_metadata["cellarr_original_gene_set"] = [",".join(genes) for genes in gene_sets]
|
|
51
|
+
|
|
52
|
+
# Add any custom metadata if provided in options
|
|
53
|
+
custom_metadata = args.get("sample_options", {}).get("metadata", {})
|
|
54
|
+
for sample, metadata in custom_metadata.items():
|
|
55
|
+
for key, value in metadata.items():
|
|
56
|
+
if key not in sample_metadata.columns:
|
|
57
|
+
sample_metadata[key] = None
|
|
58
|
+
sample_idx = sample_metadata.index[sample_metadata["cellarr_sample"] == sample][0]
|
|
59
|
+
sample_metadata.at[sample_idx, key] = value
|
|
60
|
+
|
|
61
|
+
# Create TileDB store
|
|
62
|
+
create_tiledb_frame_from_dataframe(f"{args['output_dir']}/sample_metadata", sample_metadata)
|
|
63
|
+
|
|
64
|
+
# Save metadata for subsequent steps
|
|
65
|
+
metadata = {"num_samples": len(sample_names), "total_cells": int(sample_metadata["cellarr_cell_counts"].sum())}
|
|
66
|
+
|
|
67
|
+
with open(f"{args['temp_dir']}/sample_metadata.json", "w") as f:
|
|
68
|
+
json.dump(metadata, f)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
if len(sys.argv) != 2:
|
|
73
|
+
print("Usage: python process_sample_metadata.py '<json_args>'")
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
|
|
76
|
+
process_sample_metadata(sys.argv[1])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cellarr
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1.dev1
|
|
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
|
|
@@ -10,7 +10,7 @@ Project-URL: Documentation, https://github.com/BiocPy/cellarr
|
|
|
10
10
|
Platform: any
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: Programming Language :: Python
|
|
13
|
-
Requires-Python: >=3.
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
14
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
15
15
|
License-File: LICENSE.txt
|
|
16
16
|
Requires-Dist: importlib-metadata; python_version < "3.8"
|
|
@@ -45,6 +45,17 @@ src/cellarr.egg-info/dependency_links.txt
|
|
|
45
45
|
src/cellarr.egg-info/not-zip-safe
|
|
46
46
|
src/cellarr.egg-info/requires.txt
|
|
47
47
|
src/cellarr.egg-info/top_level.txt
|
|
48
|
+
src/cellarr/slurm/README.md
|
|
49
|
+
src/cellarr/slurm/__init__.py
|
|
50
|
+
src/cellarr/slurm/build_cellarr_steps.py
|
|
51
|
+
src/cellarr/slurm/build_cellarr_steps_bkp.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
|