mirna-toolkit 0.1.0__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.
- mirna_toolkit-0.1.0/PKG-INFO +147 -0
- mirna_toolkit-0.1.0/README.md +111 -0
- mirna_toolkit-0.1.0/pyproject.toml +70 -0
- mirna_toolkit-0.1.0/setup.cfg +4 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/__init__.py +17 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/alignment/__init__.py +6 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/alignment/bowtie.py +21 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/alignment/hisat2.py +21 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/alignment/star.py +37 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/batch.py +178 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/cli.py +190 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/disease/__init__.py +5 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/disease/hmdd.py +51 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/disease/mircancer.py +21 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/io/__init__.py +21 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/io/bam.py +32 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/io/downloaders.py +27 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/io/fastq.py +41 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/io/gtf.py +41 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/ml/__init__.py +5 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/ml/classifiers.py +22 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/ml/explainability.py +26 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/ml/preprocessing.py +11 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/pipeline.py +120 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/prediction/__init__.py +16 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/prediction/confidence.py +18 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/prediction/mirecords_api.py +21 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/prediction/mirtarbase_api.py +21 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/prediction/rnahybrid.py +15 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/prediction/seed_match.py +11 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/prediction/targetscan_api.py +27 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/quantification/__init__.py +15 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/quantification/counts.py +19 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/quantification/normalization.py +42 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/quantification/novel.py +40 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/utils/__init__.py +17 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/utils/config.py +31 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/utils/http.py +77 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/utils/logging.py +13 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/utils/plugins.py +22 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/utils/versioning.py +29 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/visualization/__init__.py +17 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/visualization/heatmaps.py +8 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/visualization/networks.py +82 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/visualization/pathway.py +12 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit/visualization/volcano.py +15 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit.egg-info/PKG-INFO +147 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit.egg-info/SOURCES.txt +60 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit.egg-info/dependency_links.txt +1 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit.egg-info/entry_points.txt +2 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit.egg-info/requires.txt +20 -0
- mirna_toolkit-0.1.0/src/mirna_toolkit.egg-info/top_level.txt +1 -0
- mirna_toolkit-0.1.0/tests/test_batch.py +60 -0
- mirna_toolkit-0.1.0/tests/test_cli.py +54 -0
- mirna_toolkit-0.1.0/tests/test_external_wrappers.py +67 -0
- mirna_toolkit-0.1.0/tests/test_http_utils.py +34 -0
- mirna_toolkit-0.1.0/tests/test_normalization.py +12 -0
- mirna_toolkit-0.1.0/tests/test_novel.py +9 -0
- mirna_toolkit-0.1.0/tests/test_pipeline.py +39 -0
- mirna_toolkit-0.1.0/tests/test_plugins.py +12 -0
- mirna_toolkit-0.1.0/tests/test_prediction.py +12 -0
- mirna_toolkit-0.1.0/tests/test_versioning.py +12 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mirna-toolkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python-native end-to-end toolkit for miRNA analysis
|
|
5
|
+
Author: miRNA-Toolkit Contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: mirna,bioinformatics,rna,genomics,small-rna
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: requests>=2.31
|
|
19
|
+
Requires-Dist: numpy>=1.24
|
|
20
|
+
Requires-Dist: pandas>=2.0
|
|
21
|
+
Requires-Dist: networkx>=3.2
|
|
22
|
+
Requires-Dist: plotly>=5.20
|
|
23
|
+
Requires-Dist: matplotlib>=3.8
|
|
24
|
+
Requires-Dist: seaborn>=0.13
|
|
25
|
+
Provides-Extra: full
|
|
26
|
+
Requires-Dist: pysam>=0.22; extra == "full"
|
|
27
|
+
Requires-Dist: scikit-learn>=1.4; extra == "full"
|
|
28
|
+
Requires-Dist: shap>=0.45; extra == "full"
|
|
29
|
+
Requires-Dist: lime>=0.2.0.1; extra == "full"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
36
|
+
|
|
37
|
+
# miRNA-Toolkit
|
|
38
|
+
|
|
39
|
+
miRNA-Toolkit is a Python-native library for end-to-end miRNA analysis, including preprocessing, alignment, quantification, target prediction, disease association, and visualization.
|
|
40
|
+
|
|
41
|
+
Install name: `mirna-toolkit`
|
|
42
|
+
Import name: `mirna_toolkit`
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- I/O support for FASTQ, FASTA, BAM, and GTF/GFF-oriented workflows.
|
|
47
|
+
- Wrappers for FastQC, fastp, Bowtie, HISAT2, and STAR (small-RNA mode).
|
|
48
|
+
- Quantification and normalization (TPM, RPM, DESeq2-style size factors).
|
|
49
|
+
- Target prediction (seed match + RNAhybrid-style scoring + external connectors).
|
|
50
|
+
- Disease association integration (HMDD, miRCancer) and network export.
|
|
51
|
+
- Visualization utilities for interaction networks, heatmaps, volcano plots, and pathway enrichment plots.
|
|
52
|
+
- Optional ML utilities for classification, feature ranking, and explainability.
|
|
53
|
+
- End-to-end reproducible workflow runner (`pipeline.run_end_to_end`) and CLI (`mirna-toolkit`).
|
|
54
|
+
- Manifest-based batch workflow for multiple samples (`batch.run_batch_workflow`).
|
|
55
|
+
- Retry-aware HTTP connectors with configurable strict mode for robust integrations.
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install mirna-toolkit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For local development:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install -e .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Optional extras:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install -e .[full,dev]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from mirna_toolkit import alignment, quantification, prediction, disease, visualization
|
|
79
|
+
|
|
80
|
+
bam_file = alignment.bowtie.align("sample.fastq", reference="mirbase.fa")
|
|
81
|
+
counts = quantification.counts.from_bam(bam_file, annotation="mirbase.gtf")
|
|
82
|
+
normalized = quantification.normalization.tpm(counts)
|
|
83
|
+
|
|
84
|
+
targets = prediction.targetscan_api.get_targets("hsa-miR-21")
|
|
85
|
+
assoc = disease.hmdd.get_associations("hsa-miR-21")
|
|
86
|
+
|
|
87
|
+
fig = visualization.networks.plot_mirna_targets("hsa-miR-21", targets)
|
|
88
|
+
fig.show()
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## End-to-End Pipeline
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from mirna_toolkit.pipeline import PipelineConfig, run_end_to_end
|
|
95
|
+
|
|
96
|
+
config = PipelineConfig(
|
|
97
|
+
fastq_path="sample.fastq",
|
|
98
|
+
reference="mirbase_index",
|
|
99
|
+
annotation="mirbase.gtf",
|
|
100
|
+
output_dir="run_001",
|
|
101
|
+
aligner="bowtie",
|
|
102
|
+
normalization_method="tpm",
|
|
103
|
+
run_qc=True,
|
|
104
|
+
trim=True,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
result = run_end_to_end(config, database_versions={"miRBase": "22.1", "TargetScan": "8.0"})
|
|
108
|
+
print(result.metadata_path)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## CLI
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
mirna-toolkit --fastq sample.fastq --reference mirbase_index --annotation mirbase.gtf --output-dir run_001 --aligner bowtie --normalize tpm --run-qc --trim
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Helpful CLI topics:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
mirna-toolkit help
|
|
121
|
+
mirna-toolkit help run
|
|
122
|
+
mirna-toolkit help batch
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Batch Workflow
|
|
126
|
+
|
|
127
|
+
Manifest example (`samples.csv`):
|
|
128
|
+
|
|
129
|
+
```csv
|
|
130
|
+
sample_id,fastq_path,reference,annotation,run_qc,trim
|
|
131
|
+
s1,s1.fastq,mirbase_index,mirbase.gtf,true,true
|
|
132
|
+
s2,s2.fastq,mirbase_index,mirbase.gtf,false,false
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Run it with:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
mirna-toolkit batch --manifest samples.csv --output-dir batch_runs
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Reproducibility
|
|
142
|
+
|
|
143
|
+
Use `mirna_toolkit.utils.versioning.DatabaseVersionTracker` to record exact versions/releases of external databases used in analyses.
|
|
144
|
+
|
|
145
|
+
## Status
|
|
146
|
+
|
|
147
|
+
This repository provides a production-ready scaffold with working core APIs and wrappers. External command wrappers require the corresponding tools (for example, `bowtie`, `fastqc`, `fastp`) to be installed and available on `PATH`.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# miRNA-Toolkit
|
|
2
|
+
|
|
3
|
+
miRNA-Toolkit is a Python-native library for end-to-end miRNA analysis, including preprocessing, alignment, quantification, target prediction, disease association, and visualization.
|
|
4
|
+
|
|
5
|
+
Install name: `mirna-toolkit`
|
|
6
|
+
Import name: `mirna_toolkit`
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- I/O support for FASTQ, FASTA, BAM, and GTF/GFF-oriented workflows.
|
|
11
|
+
- Wrappers for FastQC, fastp, Bowtie, HISAT2, and STAR (small-RNA mode).
|
|
12
|
+
- Quantification and normalization (TPM, RPM, DESeq2-style size factors).
|
|
13
|
+
- Target prediction (seed match + RNAhybrid-style scoring + external connectors).
|
|
14
|
+
- Disease association integration (HMDD, miRCancer) and network export.
|
|
15
|
+
- Visualization utilities for interaction networks, heatmaps, volcano plots, and pathway enrichment plots.
|
|
16
|
+
- Optional ML utilities for classification, feature ranking, and explainability.
|
|
17
|
+
- End-to-end reproducible workflow runner (`pipeline.run_end_to_end`) and CLI (`mirna-toolkit`).
|
|
18
|
+
- Manifest-based batch workflow for multiple samples (`batch.run_batch_workflow`).
|
|
19
|
+
- Retry-aware HTTP connectors with configurable strict mode for robust integrations.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install mirna-toolkit
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For local development:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install -e .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Optional extras:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install -e .[full,dev]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from mirna_toolkit import alignment, quantification, prediction, disease, visualization
|
|
43
|
+
|
|
44
|
+
bam_file = alignment.bowtie.align("sample.fastq", reference="mirbase.fa")
|
|
45
|
+
counts = quantification.counts.from_bam(bam_file, annotation="mirbase.gtf")
|
|
46
|
+
normalized = quantification.normalization.tpm(counts)
|
|
47
|
+
|
|
48
|
+
targets = prediction.targetscan_api.get_targets("hsa-miR-21")
|
|
49
|
+
assoc = disease.hmdd.get_associations("hsa-miR-21")
|
|
50
|
+
|
|
51
|
+
fig = visualization.networks.plot_mirna_targets("hsa-miR-21", targets)
|
|
52
|
+
fig.show()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## End-to-End Pipeline
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from mirna_toolkit.pipeline import PipelineConfig, run_end_to_end
|
|
59
|
+
|
|
60
|
+
config = PipelineConfig(
|
|
61
|
+
fastq_path="sample.fastq",
|
|
62
|
+
reference="mirbase_index",
|
|
63
|
+
annotation="mirbase.gtf",
|
|
64
|
+
output_dir="run_001",
|
|
65
|
+
aligner="bowtie",
|
|
66
|
+
normalization_method="tpm",
|
|
67
|
+
run_qc=True,
|
|
68
|
+
trim=True,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
result = run_end_to_end(config, database_versions={"miRBase": "22.1", "TargetScan": "8.0"})
|
|
72
|
+
print(result.metadata_path)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## CLI
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
mirna-toolkit --fastq sample.fastq --reference mirbase_index --annotation mirbase.gtf --output-dir run_001 --aligner bowtie --normalize tpm --run-qc --trim
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Helpful CLI topics:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
mirna-toolkit help
|
|
85
|
+
mirna-toolkit help run
|
|
86
|
+
mirna-toolkit help batch
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Batch Workflow
|
|
90
|
+
|
|
91
|
+
Manifest example (`samples.csv`):
|
|
92
|
+
|
|
93
|
+
```csv
|
|
94
|
+
sample_id,fastq_path,reference,annotation,run_qc,trim
|
|
95
|
+
s1,s1.fastq,mirbase_index,mirbase.gtf,true,true
|
|
96
|
+
s2,s2.fastq,mirbase_index,mirbase.gtf,false,false
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Run it with:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
mirna-toolkit batch --manifest samples.csv --output-dir batch_runs
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Reproducibility
|
|
106
|
+
|
|
107
|
+
Use `mirna_toolkit.utils.versioning.DatabaseVersionTracker` to record exact versions/releases of external databases used in analyses.
|
|
108
|
+
|
|
109
|
+
## Status
|
|
110
|
+
|
|
111
|
+
This repository provides a production-ready scaffold with working core APIs and wrappers. External command wrappers require the corresponding tools (for example, `bowtie`, `fastqc`, `fastp`) to be installed and available on `PATH`.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mirna-toolkit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python-native end-to-end toolkit for miRNA analysis"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "miRNA-Toolkit Contributors" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["mirna", "bioinformatics", "rna", "genomics", "small-rna"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics"
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"requests>=2.31",
|
|
28
|
+
"numpy>=1.24",
|
|
29
|
+
"pandas>=2.0",
|
|
30
|
+
"networkx>=3.2",
|
|
31
|
+
"plotly>=5.20",
|
|
32
|
+
"matplotlib>=3.8",
|
|
33
|
+
"seaborn>=0.13"
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
full = [
|
|
38
|
+
"pysam>=0.22",
|
|
39
|
+
"scikit-learn>=1.4",
|
|
40
|
+
"shap>=0.45",
|
|
41
|
+
"lime>=0.2.0.1"
|
|
42
|
+
]
|
|
43
|
+
dev = [
|
|
44
|
+
"build>=1.2",
|
|
45
|
+
"pytest>=8.0",
|
|
46
|
+
"pytest-cov>=5.0",
|
|
47
|
+
"ruff>=0.5",
|
|
48
|
+
"mypy>=1.10"
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[project.scripts]
|
|
52
|
+
mirna-toolkit = "mirna_toolkit.cli:main"
|
|
53
|
+
|
|
54
|
+
[tool.setuptools]
|
|
55
|
+
package-dir = {"" = "src"}
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.packages.find]
|
|
58
|
+
where = ["src"]
|
|
59
|
+
|
|
60
|
+
[tool.pytest.ini_options]
|
|
61
|
+
minversion = "8.0"
|
|
62
|
+
addopts = "-ra -q"
|
|
63
|
+
testpaths = ["tests"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 150
|
|
67
|
+
target-version = "py39"
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint]
|
|
70
|
+
select = ["E", "F", "I", "UP"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""miRNA-Toolkit public package API."""
|
|
2
|
+
|
|
3
|
+
from . import alignment, batch, disease, io, ml, pipeline, prediction, quantification, visualization
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"alignment",
|
|
7
|
+
"batch",
|
|
8
|
+
"disease",
|
|
9
|
+
"io",
|
|
10
|
+
"ml",
|
|
11
|
+
"pipeline",
|
|
12
|
+
"prediction",
|
|
13
|
+
"quantification",
|
|
14
|
+
"visualization",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def align(
|
|
6
|
+
fastq_path: str | Path,
|
|
7
|
+
reference: str | Path,
|
|
8
|
+
output_bam: str | Path | None = None,
|
|
9
|
+
threads: int = 4,
|
|
10
|
+
) -> Path:
|
|
11
|
+
"""Align reads with Bowtie and return BAM path."""
|
|
12
|
+
fastq = Path(fastq_path)
|
|
13
|
+
output = Path(output_bam) if output_bam else fastq.with_suffix(".bowtie.bam")
|
|
14
|
+
sam = output.with_suffix(".sam")
|
|
15
|
+
|
|
16
|
+
subprocess.run(
|
|
17
|
+
["bowtie", "-p", str(threads), "-S", str(reference), str(fastq), str(sam)],
|
|
18
|
+
check=True,
|
|
19
|
+
)
|
|
20
|
+
subprocess.run(["samtools", "view", "-bS", str(sam), "-o", str(output)], check=True)
|
|
21
|
+
return output
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def align(
|
|
6
|
+
fastq_path: str | Path,
|
|
7
|
+
reference: str | Path,
|
|
8
|
+
output_bam: str | Path | None = None,
|
|
9
|
+
threads: int = 4,
|
|
10
|
+
) -> Path:
|
|
11
|
+
"""Align reads with HISAT2 and return BAM path."""
|
|
12
|
+
fastq = Path(fastq_path)
|
|
13
|
+
output = Path(output_bam) if output_bam else fastq.with_suffix(".hisat2.bam")
|
|
14
|
+
sam = output.with_suffix(".sam")
|
|
15
|
+
|
|
16
|
+
subprocess.run(
|
|
17
|
+
["hisat2", "-p", str(threads), "-x", str(reference), "-U", str(fastq), "-S", str(sam)],
|
|
18
|
+
check=True,
|
|
19
|
+
)
|
|
20
|
+
subprocess.run(["samtools", "view", "-bS", str(sam), "-o", str(output)], check=True)
|
|
21
|
+
return output
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def align(
|
|
6
|
+
fastq_path: str | Path,
|
|
7
|
+
reference: str | Path,
|
|
8
|
+
output_bam: str | Path | None = None,
|
|
9
|
+
threads: int = 4,
|
|
10
|
+
small_rna_mode: bool = True,
|
|
11
|
+
) -> Path:
|
|
12
|
+
"""Align reads with STAR in small-RNA-friendly settings and return BAM path."""
|
|
13
|
+
fastq = Path(fastq_path)
|
|
14
|
+
output = Path(output_bam) if output_bam else fastq.with_suffix(".star.bam")
|
|
15
|
+
prefix = output.with_suffix("")
|
|
16
|
+
|
|
17
|
+
cmd = [
|
|
18
|
+
"STAR",
|
|
19
|
+
"--genomeDir",
|
|
20
|
+
str(reference),
|
|
21
|
+
"--readFilesIn",
|
|
22
|
+
str(fastq),
|
|
23
|
+
"--runThreadN",
|
|
24
|
+
str(threads),
|
|
25
|
+
"--outSAMtype",
|
|
26
|
+
"BAM",
|
|
27
|
+
"Unsorted",
|
|
28
|
+
"--outFileNamePrefix",
|
|
29
|
+
f"{prefix}",
|
|
30
|
+
]
|
|
31
|
+
if small_rna_mode:
|
|
32
|
+
cmd.extend(["--alignIntronMax", "1", "--outFilterMismatchNmax", "1"])
|
|
33
|
+
|
|
34
|
+
subprocess.run(cmd, check=True)
|
|
35
|
+
produced_bam = Path(f"{prefix}Aligned.out.bam")
|
|
36
|
+
produced_bam.replace(output)
|
|
37
|
+
return output
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import json
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from . import pipeline
|
|
8
|
+
from .pipeline import PipelineConfig, PipelineResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class BatchSampleConfig:
|
|
13
|
+
sample_id: str
|
|
14
|
+
fastq_path: str | Path
|
|
15
|
+
reference: str | Path
|
|
16
|
+
annotation: str | Path
|
|
17
|
+
output_dir: str | Path | None = None
|
|
18
|
+
aligner: str = "bowtie"
|
|
19
|
+
normalization_method: str = "tpm"
|
|
20
|
+
threads: int = 4
|
|
21
|
+
run_qc: bool = False
|
|
22
|
+
trim: bool = False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class BatchSampleResult:
|
|
27
|
+
sample_id: str
|
|
28
|
+
bam_path: Path
|
|
29
|
+
counts_path: Path
|
|
30
|
+
normalized_path: Path
|
|
31
|
+
metadata_path: Path
|
|
32
|
+
counts: dict[str, int]
|
|
33
|
+
normalized: dict[str, float]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class BatchRunResult:
|
|
38
|
+
manifest_path: Path
|
|
39
|
+
summary_json_path: Path
|
|
40
|
+
summary_csv_path: Path
|
|
41
|
+
samples: list[BatchSampleResult]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def load_manifest(manifest_path: str | Path) -> list[BatchSampleConfig]:
|
|
45
|
+
path = Path(manifest_path)
|
|
46
|
+
suffix = path.suffix.lower()
|
|
47
|
+
|
|
48
|
+
if suffix == ".csv":
|
|
49
|
+
return _load_csv_manifest(path)
|
|
50
|
+
if suffix == ".json":
|
|
51
|
+
return _load_json_manifest(path)
|
|
52
|
+
raise ValueError("manifest_path must end with .csv or .json")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def run_batch_workflow(
|
|
56
|
+
manifest_path: str | Path,
|
|
57
|
+
output_dir: str | Path | None = None,
|
|
58
|
+
database_versions: dict[str, str] | None = None,
|
|
59
|
+
) -> BatchRunResult:
|
|
60
|
+
manifest_file = Path(manifest_path)
|
|
61
|
+
base_output_dir = Path(output_dir) if output_dir is not None else manifest_file.parent / "batch_outputs"
|
|
62
|
+
base_output_dir.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
|
|
64
|
+
sample_configs = load_manifest(manifest_file)
|
|
65
|
+
sample_results: list[BatchSampleResult] = []
|
|
66
|
+
|
|
67
|
+
for sample in sample_configs:
|
|
68
|
+
sample_output_dir = Path(sample.output_dir) if sample.output_dir is not None else base_output_dir / sample.sample_id
|
|
69
|
+
config = PipelineConfig(
|
|
70
|
+
fastq_path=sample.fastq_path,
|
|
71
|
+
reference=sample.reference,
|
|
72
|
+
annotation=sample.annotation,
|
|
73
|
+
output_dir=sample_output_dir,
|
|
74
|
+
aligner=sample.aligner,
|
|
75
|
+
normalization_method=sample.normalization_method,
|
|
76
|
+
threads=sample.threads,
|
|
77
|
+
run_qc=sample.run_qc,
|
|
78
|
+
trim=sample.trim,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
result = pipeline.run_end_to_end(config, database_versions=database_versions)
|
|
82
|
+
sample_results.append(_to_sample_result(sample.sample_id, result))
|
|
83
|
+
|
|
84
|
+
summary_json_path = base_output_dir / "batch_summary.json"
|
|
85
|
+
summary_csv_path = base_output_dir / "batch_summary.csv"
|
|
86
|
+
_write_summary(sample_results, summary_json_path, summary_csv_path)
|
|
87
|
+
|
|
88
|
+
return BatchRunResult(
|
|
89
|
+
manifest_path=manifest_file,
|
|
90
|
+
summary_json_path=summary_json_path,
|
|
91
|
+
summary_csv_path=summary_csv_path,
|
|
92
|
+
samples=sample_results,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _load_csv_manifest(path: Path) -> list[BatchSampleConfig]:
|
|
97
|
+
with path.open("r", encoding="utf-8", newline="") as handle:
|
|
98
|
+
reader = csv.DictReader(handle)
|
|
99
|
+
rows = list(reader)
|
|
100
|
+
return [_row_to_sample(row, index=index) for index, row in enumerate(rows, start=1)]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _load_json_manifest(path: Path) -> list[BatchSampleConfig]:
|
|
104
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
105
|
+
if isinstance(payload, dict):
|
|
106
|
+
rows = payload.get("samples", [])
|
|
107
|
+
elif isinstance(payload, list):
|
|
108
|
+
rows = payload
|
|
109
|
+
else:
|
|
110
|
+
raise ValueError("JSON manifest must be a list or a dict with a samples key")
|
|
111
|
+
return [_row_to_sample(row, index=index) for index, row in enumerate(rows, start=1)]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _row_to_sample(row: dict[str, Any], index: int) -> BatchSampleConfig:
|
|
115
|
+
sample_id = str(row.get("sample_id") or row.get("sample") or row.get("id") or f"sample_{index}")
|
|
116
|
+
fastq_path = row.get("fastq_path") or row.get("fastq")
|
|
117
|
+
reference = row.get("reference")
|
|
118
|
+
annotation = row.get("annotation")
|
|
119
|
+
|
|
120
|
+
if not fastq_path or not reference or not annotation:
|
|
121
|
+
raise ValueError("Each manifest row must include fastq_path, reference, and annotation")
|
|
122
|
+
|
|
123
|
+
return BatchSampleConfig(
|
|
124
|
+
sample_id=sample_id,
|
|
125
|
+
fastq_path=fastq_path,
|
|
126
|
+
reference=reference,
|
|
127
|
+
annotation=annotation,
|
|
128
|
+
output_dir=row.get("output_dir") or None,
|
|
129
|
+
aligner=str(row.get("aligner") or "bowtie"),
|
|
130
|
+
normalization_method=str(row.get("normalization_method") or row.get("normalize") or "tpm"),
|
|
131
|
+
threads=int(row.get("threads") or 4),
|
|
132
|
+
run_qc=_parse_bool(row.get("run_qc") or row.get("qc") or False),
|
|
133
|
+
trim=_parse_bool(row.get("trim") or False),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _to_sample_result(sample_id: str, result: PipelineResult) -> BatchSampleResult:
|
|
138
|
+
return BatchSampleResult(
|
|
139
|
+
sample_id=sample_id,
|
|
140
|
+
bam_path=result.bam_path,
|
|
141
|
+
counts_path=result.counts_path,
|
|
142
|
+
normalized_path=result.normalized_path,
|
|
143
|
+
metadata_path=result.metadata_path,
|
|
144
|
+
counts=result.counts,
|
|
145
|
+
normalized=result.normalized,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _write_summary(samples: list[BatchSampleResult], json_path: Path, csv_path: Path) -> None:
|
|
150
|
+
summary_rows = [
|
|
151
|
+
{
|
|
152
|
+
"sample_id": sample.sample_id,
|
|
153
|
+
"bam_path": str(sample.bam_path),
|
|
154
|
+
"counts_path": str(sample.counts_path),
|
|
155
|
+
"normalized_path": str(sample.normalized_path),
|
|
156
|
+
"metadata_path": str(sample.metadata_path),
|
|
157
|
+
}
|
|
158
|
+
for sample in samples
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
json_path.write_text(json.dumps(summary_rows, indent=2), encoding="utf-8")
|
|
162
|
+
|
|
163
|
+
with csv_path.open("w", encoding="utf-8", newline="") as handle:
|
|
164
|
+
writer = csv.DictWriter(handle, fieldnames=["sample_id", "bam_path", "counts_path", "normalized_path", "metadata_path"])
|
|
165
|
+
writer.writeheader()
|
|
166
|
+
writer.writerows(summary_rows)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _parse_bool(value: Any) -> bool:
|
|
170
|
+
if isinstance(value, bool):
|
|
171
|
+
return value
|
|
172
|
+
if value is None:
|
|
173
|
+
return False
|
|
174
|
+
if isinstance(value, (int, float)):
|
|
175
|
+
return value != 0
|
|
176
|
+
|
|
177
|
+
normalized = str(value).strip().lower()
|
|
178
|
+
return normalized in {"1", "true", "yes", "y", "on"}
|