ophist 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.
Files changed (80) hide show
  1. ophist-0.1.0/.gitignore +6 -0
  2. ophist-0.1.0/LICENSE +30 -0
  3. ophist-0.1.0/PKG-INFO +146 -0
  4. ophist-0.1.0/README.md +121 -0
  5. ophist-0.1.0/Snakefile +110 -0
  6. ophist-0.1.0/config/config.yml +128 -0
  7. ophist-0.1.0/config/samples.tsv +3 -0
  8. ophist-0.1.0/containers/conda_env.def +28 -0
  9. ophist-0.1.0/docs/cli.md +83 -0
  10. ophist-0.1.0/docs/config.md +305 -0
  11. ophist-0.1.0/docs/containers.md +69 -0
  12. ophist-0.1.0/docs/outputs.md +84 -0
  13. ophist-0.1.0/envs/dge.yml +15 -0
  14. ophist-0.1.0/envs/spatial.yml +24 -0
  15. ophist-0.1.0/envs/spatialrna.yml +24 -0
  16. ophist-0.1.0/examples/README.md +27 -0
  17. ophist-0.1.0/examples/atera_kidney/README.md +17 -0
  18. ophist-0.1.0/examples/atera_kidney/config/config.yml +110 -0
  19. ophist-0.1.0/examples/atera_kidney/config/samples.tsv +2 -0
  20. ophist-0.1.0/examples/ophist_logo/README.md +32 -0
  21. ophist-0.1.0/examples/ophist_logo/config/config.yml +110 -0
  22. ophist-0.1.0/examples/ophist_logo/config/samples.tsv +2 -0
  23. ophist-0.1.0/examples/xenium_tma/README.md +12 -0
  24. ophist-0.1.0/examples/xenium_tma/config/config.yml +110 -0
  25. ophist-0.1.0/examples/xenium_tma/config/samples.tsv +5 -0
  26. ophist-0.1.0/profiles/local/config.yaml +9 -0
  27. ophist-0.1.0/profiles/slurm/config.yaml +21 -0
  28. ophist-0.1.0/pyproject.toml +105 -0
  29. ophist-0.1.0/rules/01_preprocessing.smk +114 -0
  30. ophist-0.1.0/rules/02_tissue_detection.smk +66 -0
  31. ophist-0.1.0/rules/03_spatialrna.smk +152 -0
  32. ophist-0.1.0/rules/04_lumen_detection.smk +73 -0
  33. ophist-0.1.0/rules/06_smoothing.smk +44 -0
  34. ophist-0.1.0/rules/07_hmrf_potts.smk +86 -0
  35. ophist-0.1.0/rules/08_domain_analysis.smk +41 -0
  36. ophist-0.1.0/rules/09_dge.smk +28 -0
  37. ophist-0.1.0/rules/10_report.smk +231 -0
  38. ophist-0.1.0/rules/resources.smk +182 -0
  39. ophist-0.1.0/scripts/01_preprocessing/preprocess_atera.py +87 -0
  40. ophist-0.1.0/scripts/01_preprocessing/preprocess_table.py +62 -0
  41. ophist-0.1.0/scripts/01_preprocessing/preprocess_xenium.py +83 -0
  42. ophist-0.1.0/scripts/01_preprocessing/tables.py +128 -0
  43. ophist-0.1.0/scripts/02_tissue_detection/filter_tissue_transcripts.py +42 -0
  44. ophist-0.1.0/scripts/02_tissue_detection/init_cohort_zarr.py +11 -0
  45. ophist-0.1.0/scripts/02_tissue_detection/tissue_detection.py +67 -0
  46. ophist-0.1.0/scripts/03_spatialrna/build_graphs.py +96 -0
  47. ophist-0.1.0/scripts/03_spatialrna/collect_embeddings.py +20 -0
  48. ophist-0.1.0/scripts/03_spatialrna/embed.py +112 -0
  49. ophist-0.1.0/scripts/03_spatialrna/sr_common.py +258 -0
  50. ophist-0.1.0/scripts/03_spatialrna/train.py +93 -0
  51. ophist-0.1.0/scripts/03_spatialrna/write_gene_encoding.py +14 -0
  52. ophist-0.1.0/scripts/04_lumen_detection/lumen_detection.py +109 -0
  53. ophist-0.1.0/scripts/04_lumen_detection/plot_lumen.py +28 -0
  54. ophist-0.1.0/scripts/04_lumen_detection/plot_lumen_collate.py +28 -0
  55. ophist-0.1.0/scripts/04_lumen_detection/plot_tx_lumen.py +34 -0
  56. ophist-0.1.0/scripts/06_smoothing/plot_smoothed.py +10 -0
  57. ophist-0.1.0/scripts/06_smoothing/smooth_embeddings.py +23 -0
  58. ophist-0.1.0/scripts/07_hmrf_potts/hmrf_potts.py +39 -0
  59. ophist-0.1.0/scripts/07_hmrf_potts/palette.py +41 -0
  60. ophist-0.1.0/scripts/07_hmrf_potts/plot_hmrf.py +18 -0
  61. ophist-0.1.0/scripts/07_hmrf_potts/plot_hmrf_sample.py +40 -0
  62. ophist-0.1.0/scripts/08_domain_analysis/domain_counts.py +31 -0
  63. ophist-0.1.0/scripts/08_domain_analysis/domain_de_gsea.R +91 -0
  64. ophist-0.1.0/scripts/09_dge/condition_de_gsea.R +96 -0
  65. ophist-0.1.0/scripts/09_dge/contrast_utils.R +41 -0
  66. ophist-0.1.0/scripts/10_report/assets/report.css +161 -0
  67. ophist-0.1.0/scripts/10_report/assets/report.js +435 -0
  68. ophist-0.1.0/scripts/10_report/build_report.py +114 -0
  69. ophist-0.1.0/scripts/10_report/composition.py +18 -0
  70. ophist-0.1.0/scripts/10_report/embedding_figures.py +305 -0
  71. ophist-0.1.0/scripts/10_report/report_common.py +266 -0
  72. ophist-0.1.0/scripts/10_report/sample_figures.py +104 -0
  73. ophist-0.1.0/scripts/10_report/sections.py +856 -0
  74. ophist-0.1.0/scripts/10_report/slide_qc.py +237 -0
  75. ophist-0.1.0/scripts/utils/common.py +58 -0
  76. ophist-0.1.0/scripts/utils/pseudobulk.R +44 -0
  77. ophist-0.1.0/src/ophist/__init__.py +3 -0
  78. ophist-0.1.0/src/ophist/cli.py +224 -0
  79. ophist-0.1.0/src/ophist/paths.py +13 -0
  80. ophist-0.1.0/tests/test_cli.py +126 -0
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ .snakemake/
3
+ containers/build/
4
+ data/
5
+ output/
6
+ logs/
ophist-0.1.0/LICENSE ADDED
@@ -0,0 +1,30 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Patrick Crock and the OphiST authors
4
+ (St Vincent's Institute of Medical Research)
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ this list of conditions and the following disclaimer in the documentation
15
+ and/or other materials provided with the distribution.
16
+
17
+ 3. Neither the name of the copyright holder nor the names of its
18
+ contributors may be used to endorse or promote products derived from
19
+ this software without specific prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
ophist-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.5
2
+ Name: ophist
3
+ Version: 0.1.0
4
+ Summary: End-to-end, segmentation-free analysis for image-based spatial transcriptomics (Xenium, Atera, CosMx, MERSCOPE): a Snakemake pipeline with a command line.
5
+ Project-URL: Repository, https://gitlab.svi.edu.au/biocellgen-public/ophist
6
+ Author-email: Patrick Crock <pat.crock@svi.edu.au>, Yan Pan <anne.pan@svi.edu.au>
7
+ License-Expression: BSD-3-Clause
8
+ License-File: LICENSE
9
+ Keywords: CosMx,MERSCOPE,Snakemake,Xenium,domains,imaging spatial transcriptomics,niches,spatial domains,spatial transcriptomics
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: pandas
18
+ Requires-Dist: pyyaml
19
+ Requires-Dist: snakemake<10,>=9
20
+ Provides-Extra: slurm
21
+ Requires-Dist: snakemake-executor-plugin-slurm; extra == 'slurm'
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=7; extra == 'test'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Overview
27
+
28
+ OphiST is a Snakemake pipeline for the analysis of image-based spatial transcriptomics (iST) data in a segmentation-free manner.
29
+
30
+ The accurate assignment of transcripts to cells presents a major bottleneck in conventional analysis of iST data. Issues such as transcript bleed from adjacent cells, cells overlapping in the z-plane, and segmentation failures on irregularly-shaped cells can all lead to complications in downstream analyses. OphiST applies several tools from the Bioinformatics and Cellular Genomics Lab at SVI designed to operate directly on the raw, individual transcript data output by iST platforms, with no cell segmentation required. Namely, [SpatialRNA](https://pypi.org/project/spatialrna/) applies graph neural network (GNN) to embed each individual transcript in a latent space reflecting its local molecular context, and [DuetiST](https://pypi.org/project/duetist/) then derives both fine-grained molecular niches and broader spatial domains from the transcript-embedding data. These tools both operate downstream of sensible preprocessing steps such as demultiplexing, QC filtering (if possible), and tissue-detection. Further along the pipeline, basic downstream analyses such as domain-wise (and condition-wise, if contrasts are supplied) differential gene expression and gene-set enrichment analysis are performed. Everything is summarised in a self-contained HTML report output by the final step of the pipeline.
31
+
32
+ ## Documentation
33
+
34
+ See the following sub-pages for further details about the pipeline:
35
+
36
+ - [Configuration](docs/config.md) explains every parameter in the `config` should you wish to tweak the defaults.
37
+ - [Outputs](docs/outputs.md) details the outputs of the pipeline.
38
+ - [Containers](docs/containers.md) contains information about the Apptainer containers and Conda environments used for running the pipeline, including instructions on building them yourself.
39
+ - [Command line](docs/cli.md) describes the `ophist` command: `run`, `init`, `examples` and `profile`.
40
+
41
+ ## Installation
42
+
43
+ OphiST needs Python 3.11 or later. Install it with pip, including the SLURM plugin if you run
44
+ on a SLURM cluster:
45
+
46
+ ```bash
47
+ pip install "ophist[slurm]"
48
+ ```
49
+
50
+ This installs the `ophist` command, the workflow and [Snakemake](https://snakemake.readthedocs.io).
51
+ The latest development version installs straight from the repository:
52
+ `pip install "ophist[slurm] @ git+https://gitlab.svi.edu.au/biocellgen-public/ophist.git"`.
53
+ On a cluster, install into an environment on a shared filesystem, because the compute nodes run
54
+ the workflow's scripts from there.
55
+
56
+ To work on the workflow itself, clone the repository. An editable install then makes `ophist`
57
+ run the clone:
58
+
59
+ ```bash
60
+ git clone https://gitlab.svi.edu.au/biocellgen-public/ophist.git
61
+ pip install -e "./ophist[slurm]"
62
+ ```
63
+
64
+ A clone also runs without pip, through Snakemake directly; see
65
+ [docs/cli.md](docs/cli.md#without-the-command).
66
+
67
+ ## Requirements
68
+
69
+ - [Apptainer](https://apptainer.org). Each step runs in a prebuilt image, pulled on first use.
70
+ Conda works too; see [docs/containers.md](docs/containers.md).
71
+ - An NVIDIA GPU for step 3 (spatialRNA training and embedding).
72
+ - To run on a SLURM cluster, the
73
+ [SLURM executor plugin](https://snakemake.github.io/snakemake-plugin-catalog/plugins/executor/slurm.html),
74
+ installed by the `slurm` extra above.
75
+
76
+ ## Quick start
77
+
78
+ 1. Start a run directory from the example closest to your data (`ophist examples` lists
79
+ them):
80
+
81
+ ```bash
82
+ ophist init my_run --example xenium_tma
83
+ ```
84
+
85
+ 2. List your samples in `my_run/config/samples.tsv`. Adjust `my_run/config/config.yml` as
86
+ needed. A config only needs the settings it changes; see [docs/config.md](docs/config.md).
87
+
88
+ 3. Once, write a Snakemake profile, and fill in your account, partition and the data paths to
89
+ bind into the images:
90
+
91
+ ```bash
92
+ ophist profile slurm # writes profiles/slurm/config.yaml
93
+ ```
94
+
95
+ On a single GPU machine, use `ophist profile local` instead, or no profile at all.
96
+
97
+ 4. Run. Output goes to `my_run/`; add `-n` first for a dry run.
98
+
99
+ ```bash
100
+ ophist run my_run/config/config.yml --profile profiles/slurm
101
+ ```
102
+
103
+ A long run is best launched from a batch job of its own. That head job needs about 1 CPU
104
+ and 8 GB.
105
+
106
+ ## Examples
107
+
108
+ [`examples/`](examples/) holds configs for three data types:
109
+
110
+ | Example | Data |
111
+ |---|---|
112
+ | [`xenium_tma`](examples/xenium_tma/) | Xenium slides cut into TMA cores, with a condition contrast |
113
+ | [`atera_kidney`](examples/atera_kidney/) | one Atera whole-transcriptome slide |
114
+ | [`ophist_logo`](examples/ophist_logo/) | the OphiST simulated dataset, a small test that runs end to end in under an hour |
115
+
116
+ ## Overview of the pipeline
117
+
118
+ | Step | Rule file | Main output | Description |
119
+ |---|---|---|---|
120
+ | 1. Preprocessing | `rules/01_preprocessing.smk` | `data/{sample}/qc_transcripts.zarr` | Raw transcripts are QC-filtered if possible. (Optional: demultiplexing of slides) |
121
+ | 2. Tissue detection | `rules/02_tissue_detection.smk` | `data/{sample}/tissue_mask.npz`, `data/cohort.zarr` | Tissue outline detected from transcript density; transcripts are filtered to this outline. |
122
+ | 3. spatialRNA embeddings | `rules/03_spatialrna.smk` | `data/spatialrna/cohort_embeddings.zarr` | SpatialRNA applies a GNN to give each transcript an embedding vector describing its spatial context. |
123
+ | 4. Lumen detection | `rules/04_lumen_detection.smk` | `data/{sample}/masks.geojson` | (Optional: Lumen within the tissue are detected from transcript density; this mask is used downstream in smoothing.) |
124
+ | 6. Smoothing | `rules/06_smoothing.smk` | `data/{sample}/smoothed.npz` | The transcript-embeddings are smoothed onto a grid for tractability. |
125
+ | 7. Niches and domains | `rules/07_hmrf_potts.smk` | `data/{sample}/{niches,domains}.npz`, `data/hmrf/` | DuetiST derives niches and domains shared across the samples from the GNN embeddings. |
126
+ | 8. Domain analysis | `rules/08_domain_analysis.smk` | `output/domain_dge/` | Transcripts are pseudobulked by domain. Domains are tested for marker genes and cell-type signatures. |
127
+ | 9. Condition DGE | `rules/09_dge.smk` | `output/condition_dge/` | (Optional: Further pseudobulk analysis of transcripts within domains, across contrasts specified in inputs.) |
128
+ | 10. Report | `rules/10_report.smk` | `output/report.html` | Summaries of all above steps, including QC figures, training and model reports, PCA analysis, spatial maps, DGE and GSEA tables. |
129
+
130
+ Each rule file's scripts are in `scripts/<rule file name>/`. Helpers shared between steps are
131
+ in `scripts/utils/`.
132
+
133
+ ## Credits
134
+
135
+ - Patrick Crock: Lead engineer of the pipeline.
136
+ - Anne Pan: Current maintainer of the [spatialrna](https://pypi.org/project/spatialrna/) package. Designed rules 03 and 10.
137
+ - Ruqian Lyu: Original author of the [spatialrna](https://pypi.org/project/spatialrna/) package.
138
+ - Smoothing and the Potts model both use [duetist](https://gitlab.svi.edu.au/biocellgen-public/duetist).
139
+
140
+ ## Citation
141
+
142
+ A manuscript is in preparation.
143
+
144
+ ## License
145
+
146
+ BSD 3-Clause; see [LICENSE](LICENSE).
ophist-0.1.0/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # Overview
2
+
3
+ OphiST is a Snakemake pipeline for the analysis of image-based spatial transcriptomics (iST) data in a segmentation-free manner.
4
+
5
+ The accurate assignment of transcripts to cells presents a major bottleneck in conventional analysis of iST data. Issues such as transcript bleed from adjacent cells, cells overlapping in the z-plane, and segmentation failures on irregularly-shaped cells can all lead to complications in downstream analyses. OphiST applies several tools from the Bioinformatics and Cellular Genomics Lab at SVI designed to operate directly on the raw, individual transcript data output by iST platforms, with no cell segmentation required. Namely, [SpatialRNA](https://pypi.org/project/spatialrna/) applies graph neural network (GNN) to embed each individual transcript in a latent space reflecting its local molecular context, and [DuetiST](https://pypi.org/project/duetist/) then derives both fine-grained molecular niches and broader spatial domains from the transcript-embedding data. These tools both operate downstream of sensible preprocessing steps such as demultiplexing, QC filtering (if possible), and tissue-detection. Further along the pipeline, basic downstream analyses such as domain-wise (and condition-wise, if contrasts are supplied) differential gene expression and gene-set enrichment analysis are performed. Everything is summarised in a self-contained HTML report output by the final step of the pipeline.
6
+
7
+ ## Documentation
8
+
9
+ See the following sub-pages for further details about the pipeline:
10
+
11
+ - [Configuration](docs/config.md) explains every parameter in the `config` should you wish to tweak the defaults.
12
+ - [Outputs](docs/outputs.md) details the outputs of the pipeline.
13
+ - [Containers](docs/containers.md) contains information about the Apptainer containers and Conda environments used for running the pipeline, including instructions on building them yourself.
14
+ - [Command line](docs/cli.md) describes the `ophist` command: `run`, `init`, `examples` and `profile`.
15
+
16
+ ## Installation
17
+
18
+ OphiST needs Python 3.11 or later. Install it with pip, including the SLURM plugin if you run
19
+ on a SLURM cluster:
20
+
21
+ ```bash
22
+ pip install "ophist[slurm]"
23
+ ```
24
+
25
+ This installs the `ophist` command, the workflow and [Snakemake](https://snakemake.readthedocs.io).
26
+ The latest development version installs straight from the repository:
27
+ `pip install "ophist[slurm] @ git+https://gitlab.svi.edu.au/biocellgen-public/ophist.git"`.
28
+ On a cluster, install into an environment on a shared filesystem, because the compute nodes run
29
+ the workflow's scripts from there.
30
+
31
+ To work on the workflow itself, clone the repository. An editable install then makes `ophist`
32
+ run the clone:
33
+
34
+ ```bash
35
+ git clone https://gitlab.svi.edu.au/biocellgen-public/ophist.git
36
+ pip install -e "./ophist[slurm]"
37
+ ```
38
+
39
+ A clone also runs without pip, through Snakemake directly; see
40
+ [docs/cli.md](docs/cli.md#without-the-command).
41
+
42
+ ## Requirements
43
+
44
+ - [Apptainer](https://apptainer.org). Each step runs in a prebuilt image, pulled on first use.
45
+ Conda works too; see [docs/containers.md](docs/containers.md).
46
+ - An NVIDIA GPU for step 3 (spatialRNA training and embedding).
47
+ - To run on a SLURM cluster, the
48
+ [SLURM executor plugin](https://snakemake.github.io/snakemake-plugin-catalog/plugins/executor/slurm.html),
49
+ installed by the `slurm` extra above.
50
+
51
+ ## Quick start
52
+
53
+ 1. Start a run directory from the example closest to your data (`ophist examples` lists
54
+ them):
55
+
56
+ ```bash
57
+ ophist init my_run --example xenium_tma
58
+ ```
59
+
60
+ 2. List your samples in `my_run/config/samples.tsv`. Adjust `my_run/config/config.yml` as
61
+ needed. A config only needs the settings it changes; see [docs/config.md](docs/config.md).
62
+
63
+ 3. Once, write a Snakemake profile, and fill in your account, partition and the data paths to
64
+ bind into the images:
65
+
66
+ ```bash
67
+ ophist profile slurm # writes profiles/slurm/config.yaml
68
+ ```
69
+
70
+ On a single GPU machine, use `ophist profile local` instead, or no profile at all.
71
+
72
+ 4. Run. Output goes to `my_run/`; add `-n` first for a dry run.
73
+
74
+ ```bash
75
+ ophist run my_run/config/config.yml --profile profiles/slurm
76
+ ```
77
+
78
+ A long run is best launched from a batch job of its own. That head job needs about 1 CPU
79
+ and 8 GB.
80
+
81
+ ## Examples
82
+
83
+ [`examples/`](examples/) holds configs for three data types:
84
+
85
+ | Example | Data |
86
+ |---|---|
87
+ | [`xenium_tma`](examples/xenium_tma/) | Xenium slides cut into TMA cores, with a condition contrast |
88
+ | [`atera_kidney`](examples/atera_kidney/) | one Atera whole-transcriptome slide |
89
+ | [`ophist_logo`](examples/ophist_logo/) | the OphiST simulated dataset, a small test that runs end to end in under an hour |
90
+
91
+ ## Overview of the pipeline
92
+
93
+ | Step | Rule file | Main output | Description |
94
+ |---|---|---|---|
95
+ | 1. Preprocessing | `rules/01_preprocessing.smk` | `data/{sample}/qc_transcripts.zarr` | Raw transcripts are QC-filtered if possible. (Optional: demultiplexing of slides) |
96
+ | 2. Tissue detection | `rules/02_tissue_detection.smk` | `data/{sample}/tissue_mask.npz`, `data/cohort.zarr` | Tissue outline detected from transcript density; transcripts are filtered to this outline. |
97
+ | 3. spatialRNA embeddings | `rules/03_spatialrna.smk` | `data/spatialrna/cohort_embeddings.zarr` | SpatialRNA applies a GNN to give each transcript an embedding vector describing its spatial context. |
98
+ | 4. Lumen detection | `rules/04_lumen_detection.smk` | `data/{sample}/masks.geojson` | (Optional: Lumen within the tissue are detected from transcript density; this mask is used downstream in smoothing.) |
99
+ | 6. Smoothing | `rules/06_smoothing.smk` | `data/{sample}/smoothed.npz` | The transcript-embeddings are smoothed onto a grid for tractability. |
100
+ | 7. Niches and domains | `rules/07_hmrf_potts.smk` | `data/{sample}/{niches,domains}.npz`, `data/hmrf/` | DuetiST derives niches and domains shared across the samples from the GNN embeddings. |
101
+ | 8. Domain analysis | `rules/08_domain_analysis.smk` | `output/domain_dge/` | Transcripts are pseudobulked by domain. Domains are tested for marker genes and cell-type signatures. |
102
+ | 9. Condition DGE | `rules/09_dge.smk` | `output/condition_dge/` | (Optional: Further pseudobulk analysis of transcripts within domains, across contrasts specified in inputs.) |
103
+ | 10. Report | `rules/10_report.smk` | `output/report.html` | Summaries of all above steps, including QC figures, training and model reports, PCA analysis, spatial maps, DGE and GSEA tables. |
104
+
105
+ Each rule file's scripts are in `scripts/<rule file name>/`. Helpers shared between steps are
106
+ in `scripts/utils/`.
107
+
108
+ ## Credits
109
+
110
+ - Patrick Crock: Lead engineer of the pipeline.
111
+ - Anne Pan: Current maintainer of the [spatialrna](https://pypi.org/project/spatialrna/) package. Designed rules 03 and 10.
112
+ - Ruqian Lyu: Original author of the [spatialrna](https://pypi.org/project/spatialrna/) package.
113
+ - Smoothing and the Potts model both use [duetist](https://gitlab.svi.edu.au/biocellgen-public/duetist).
114
+
115
+ ## Citation
116
+
117
+ A manuscript is in preparation.
118
+
119
+ ## License
120
+
121
+ BSD 3-Clause; see [LICENSE](LICENSE).
ophist-0.1.0/Snakefile ADDED
@@ -0,0 +1,110 @@
1
+ import os
2
+ import re
3
+
4
+ import pandas as pd
5
+
6
+
7
+ # Settings: the pipeline's defaults, then the run's config/config.yml when there is one, then
8
+ # any --configfile / --config given to Snakemake (`ophist run CONFIG` passes CONFIG this way).
9
+ # A run's config only needs the settings it changes.
10
+
11
+ configfile: f"{workflow.basedir}/config/config.yml"
12
+ if os.path.exists("config/config.yml"):
13
+ configfile: "config/config.yml"
14
+
15
+
16
+ # Setup sample list (or single sample name)
17
+
18
+ samples_cfg = config["samples"]
19
+
20
+ if isinstance(samples_cfg, str) and not samples_cfg.endswith(".tsv"):
21
+ SAMPLES = [samples_cfg]
22
+ SAMPLE_SHEET = None
23
+ else:
24
+ # sample_name, path (Xenium outs/ directory), optional x/y_min/max crop and contrast_N columns
25
+ SAMPLE_SHEET = pd.read_csv(samples_cfg, sep="\t", dtype={"sample_name": str, "path": str}).set_index("sample_name")
26
+ SAMPLES = SAMPLE_SHEET.index.tolist()
27
+
28
+ wildcard_constraints:
29
+ sample="|".join(re.escape(s) for s in SAMPLES)
30
+
31
+
32
+ # spatialRNA's output store: written by rule 03, or already there for prerun data.
33
+ # Everything downstream of spatialRNA reads its transcripts from here.
34
+
35
+ PRERUN = config["preprocessing"]["type"] == "prerun"
36
+ EMBEDDINGS = config["spatialrna"]["embeddings"]
37
+ EMBEDDING_ROWS = f"{EMBEDDINGS}/{{sample}}/x" # a sample's row count, for mem_by_rows()
38
+
39
+ # Apptainer images (--sdm apptainer), one per envs/*.yml; each rule's container: sits
40
+ # next to its conda: env, which --sdm conda uses instead.
41
+ CONTAINERS = config["containers"]
42
+
43
+ # Smoothing and the Potts model (rules 06, 07) come from the duetist package installed in
44
+ # the spatial env, or from a duetist checkout when duetist.src is set (docs/config.md).
45
+ DUETIST_SRC = str(config.get("duetist", {}).get("src") or "")
46
+
47
+ # The report's heading and file: report.title, and output/<title>.html with anything unsafe
48
+ # in a file name replaced. No title gives the default heading in output/report.html.
49
+ REPORT_TITLE = str(config.get("report", {}).get("title") or "").removesuffix(".html")
50
+ REPORT_FILE = re.sub(r"[^\w.-]+", "_", REPORT_TITLE).strip("_") or "report"
51
+ REPORT_HTML = f"output/{REPORT_FILE}.html"
52
+
53
+
54
+ # Condition DGE targets: only with several samples and a contrast_N column in samples.tsv
55
+
56
+ def has_condition_contrasts():
57
+ header = pd.read_csv(samples_cfg, sep="\t", nrows=0).columns
58
+ return any(re.match(r"^contrast_\d+$", c) for c in header)
59
+
60
+ def get_conditional_targets():
61
+ if len(SAMPLES) > 1 and has_condition_contrasts():
62
+ return ["output/condition_dge/condition_de.csv", "output/condition_dge/condition_gsea.csv",
63
+ "output/condition_dge/condition_de_full.csv", "output/condition_dge/condition_gsea_full.csv"]
64
+ return []
65
+
66
+
67
+ # Define all targets
68
+
69
+ rule all:
70
+ input:
71
+ expand("output/{sample}/lumen_plot.png", sample=SAMPLES),
72
+ expand("output/{sample}/tx_lumen_plot.png", sample=SAMPLES),
73
+ "output/plots/lumen_detection.png",
74
+ expand("output/{sample}/smoothed_embeddings.png", sample=SAMPLES),
75
+ expand("output/{sample}/hmrf_labels.png", sample=SAMPLES),
76
+ [f"output/plots/hmrf_{p}.png" for p in ("domains", "niches", "domain_profiles", "history", "composition")],
77
+ "output/domain_dge/domain_de.csv",
78
+ "output/domain_dge/domain_gsea.csv",
79
+ REPORT_HTML,
80
+ get_conditional_targets()
81
+
82
+ localrules: all
83
+
84
+
85
+ # Resource allocation for job submission
86
+
87
+ include: "rules/resources.smk"
88
+
89
+
90
+ # Rules proper
91
+
92
+ # 1. Preprocessing - including splitting of multiplexed samples, and filtering of low-quality/negative control spots
93
+ include: "rules/01_preprocessing.smk"
94
+ # 2. Tissue detection - Masking of the tissue area to exclude background spots
95
+ include: "rules/02_tissue_detection.smk"
96
+ # 3. SpatialRNA - production of per-transcript embeddings reflecting their local spatial context
97
+ if not PRERUN:
98
+ include: "rules/03_spatialrna.smk"
99
+ # 4. Lumen detection - detection of lumen in the tissue
100
+ include: "rules/04_lumen_detection.smk"
101
+ # 6. Smoothing - smoothing of the spatial embeddings onto a grid
102
+ include: "rules/06_smoothing.smk"
103
+ # 7. Niches and domains - a hierarchical Potts model of niches and domains across the cohort
104
+ include: "rules/07_hmrf_potts.smk"
105
+ # 8. Domain analysis - pseudobulk counts per domain, domain marker genes and GSEA
106
+ include: "rules/08_domain_analysis.smk"
107
+ # 9. DGE - differential gene expression analysis between conditions, if multiple samples and contrasts are provided
108
+ include: "rules/09_dge.smk"
109
+ # 10. Report - generation of a report summarizing the results
110
+ include: "rules/10_report.smk"
@@ -0,0 +1,128 @@
1
+ # OphiST configuration. Every setting is explained in docs/config.md;
2
+ # examples/ has configs tuned for Xenium, Atera and the OphiST simulated data.
3
+
4
+ # ---- Inputs ----
5
+ samples: "config/samples.tsv"
6
+
7
+ # ---- Software images (--sdm apptainer) ----
8
+ containers:
9
+ spatial: "oras://ghcr.io/patrickcrock/ophist-spatial:0.2.0"
10
+ spatialrna: "oras://ghcr.io/patrickcrock/ophist-spatialrna:0.1.1"
11
+ dge: "oras://ghcr.io/patrickcrock/ophist-dge:0.1.0"
12
+
13
+ # ---- General ----
14
+ global:
15
+ seed: 23
16
+ species: "human"
17
+
18
+ # ---- 1. Preprocessing ----
19
+ preprocessing:
20
+ type: "xenium"
21
+ xenium:
22
+ qv_threshold: 20
23
+ atera:
24
+ qv_threshold: 20
25
+ cosmx:
26
+ um_per_px: 0.12028
27
+ control_prefixes: ["NegPrb", "Negative", "SystemControl", "FalseCode"]
28
+ merscope:
29
+ control_prefixes: ["Blank-"]
30
+ custom:
31
+ file: ""
32
+ gene_col: "gene"
33
+ x_col: "x"
34
+ y_col: "y"
35
+ z_col: ""
36
+ qv_col: ""
37
+ count_col: ""
38
+ qv_threshold: 20
39
+ um_per_unit: 1.0
40
+ control_prefixes: []
41
+
42
+ # ---- 2 & 4. Tissue and lumen masks ----
43
+ masks:
44
+ downscaling: 0
45
+ resolution: 4
46
+ blur_sigma: 8
47
+ threshold: 0.05
48
+ min_feature_size: 250
49
+ closing_radius: 5
50
+
51
+ # ---- 3. spatialRNA embeddings ----
52
+ spatialrna:
53
+ embeddings: "data/spatialrna/cohort_embeddings.zarr"
54
+ work_dir: "data/spatialrna"
55
+ conda_env: ""
56
+ graph:
57
+ radius: 3.0
58
+ max_num_neighbors: 500
59
+ radius_method: "knn"
60
+ radius_3d: False
61
+ pad_hops: 2
62
+ tx_per_tile: 500000
63
+ num_tiles: 0
64
+ n_procs: 4
65
+ mem_per_proc_mb: 16000
66
+ threads: 8
67
+ model:
68
+ name: "gat_l2"
69
+ kwargs:
70
+ hidden_channels: 64
71
+ out_channels: 50
72
+ num_layers: 2
73
+ dropout: 0.0
74
+ training:
75
+ epochs: 5
76
+ lr: 0.001
77
+ batch_size: 2048
78
+ num_neighbors: [15, 10]
79
+ num_train_edges: 25000
80
+ val_fraction: 0.05
81
+ tiles_per_epoch: 0
82
+ mem_mb: 64000
83
+ runtime_min: 1440
84
+ embed:
85
+ method: "sampled"
86
+ num_neighbors: [15, 10]
87
+ batch_size: 2048
88
+
89
+ # ---- 6. Smoothing ----
90
+ smoothing:
91
+ grid_size: 8
92
+ sigma: 8
93
+
94
+ # ---- 7. Niches and domains (hierarchical Potts model) ----
95
+ hmrf_potts:
96
+ init: "dp"
97
+ K1: 50
98
+ K2: 25
99
+ hdbscan_niche_min_frac: 0.0005
100
+ hdbscan_domain_min_frac: 0.0005
101
+ init_n_max: 500000
102
+ burn_in: 12
103
+ n_iter: 36
104
+ radius_um: 20
105
+ n0: 0.0625
106
+ floor_um2: 800
107
+ domain_stride_um: 16
108
+ merge_h_max: 0.3
109
+ niche_h_max: 0.95
110
+ icm_max_sweeps: 50
111
+
112
+ # ---- 10. Report ----
113
+ report:
114
+ title: ""
115
+ target_mb: 200
116
+ map_px_max: 1600
117
+ embedding_points: 2000000
118
+ umap_points: 100000
119
+ max_table_rows: 1000
120
+ embedding_panels_max_samples: 32
121
+
122
+ # ---- Cluster resources ----
123
+ resources:
124
+ max_mem_mb: 1400000
125
+ gpu_max_mem_mb: 490000
126
+ gpu_partition: "gpu"
127
+ gpu_gres: "gpu:1"
128
+ gpu_slurm_extra: ""
@@ -0,0 +1,3 @@
1
+ sample_name path
2
+ sample1 /path/to/sample1/outs
3
+ sample2 /path/to/sample2/outs
@@ -0,0 +1,28 @@
1
+ # One image per conda env: envs/{{ ENV }}.yml installed into /opt/env, on PATH, so
2
+ # Snakemake's `python` / `Rscript` resolve to it. Build from the repo root (the %files
3
+ # path is relative to it), e.g.
4
+ # apptainer build --fakeroot --build-arg ENV=spatial ophist-spatial.sif containers/conda_env.def
5
+ # See docs/containers.md.
6
+ Bootstrap: docker
7
+ From: mambaorg/micromamba:2.8.1-debian12-slim
8
+
9
+ %arguments
10
+ ENV=spatial
11
+
12
+ %files
13
+ envs/{{ ENV }}.yml /opt/env.yml
14
+
15
+ %post
16
+ export MAMBA_ROOT_PREFIX=/opt/conda
17
+ # envs/spatialrna.yml's CUDA pytorch only solves where conda sees a GPU driver; the
18
+ # build runs on drivers for CUDA >= 12.4. Harmless for the other envs.
19
+ export CONDA_OVERRIDE_CUDA=12.4
20
+ micromamba create -y -p /opt/env -f /opt/env.yml
21
+ micromamba clean -afy
22
+
23
+ %environment
24
+ export PATH=/opt/env/bin:$PATH
25
+
26
+ %labels
27
+ org.opencontainers.image.source https://gitlab.svi.edu.au/biocellgen-public/ophist
28
+ env {{ ENV }}
@@ -0,0 +1,83 @@
1
+ # Command line
2
+
3
+ `pip install ophist` installs the `ophist` command together with the workflow. The command
4
+ calls Snakemake for you; everything Snakemake can do is still available through it.
5
+
6
+ On a cluster, install OphiST into an environment on a shared filesystem. The jobs run the
7
+ workflow's scripts from where the package is installed, so the compute nodes must be able to
8
+ read it.
9
+
10
+ ## `ophist run`
11
+
12
+ ```bash
13
+ ophist run CONFIG [-d DIR] [--profile PROFILE] [SNAKEMAKE OPTIONS ...]
14
+ ```
15
+
16
+ Runs the pipeline with the settings in `CONFIG`.
17
+
18
+ - **Settings.** A config only needs the settings it changes. Every other setting keeps its
19
+ default (see [config.md](config.md#partial-configs)).
20
+ - **Run directory.** This is where `data/`, `output/` and `logs/` are written, and where
21
+ relative paths in the config (such as `samples: "config/samples.tsv"`) are looked up. It is
22
+ the config's folder, or its parent when that folder is called `config`, so
23
+ `ophist run my_run/config/config.yml` runs in `my_run/`. `-d DIR` chooses another one.
24
+ - **Profile.** `--profile PROFILE` is a Snakemake profile: a folder holding a `config.yaml`, or
25
+ the name of one in `~/.config/snakemake/`. Use it to run on a cluster; see `ophist profile`
26
+ below.
27
+ - Without a profile, and without any of Snakemake's own execution options (`--sdm`,
28
+ `--executor`, `--cores`, `--jobs`), the pipeline runs on the local machine in the published
29
+ Apptainer images (`--sdm apptainer --cores all`).
30
+ - **Snakemake options.** Any other option goes straight to Snakemake. For example:
31
+ - `-n` for a dry run;
32
+ - `--jobs 50`;
33
+ - `--rerun-incomplete`;
34
+ - `--config smoothing='{"grid_size": 4}'`.
35
+ - **Binds.** The jobs' containers are given the installed workflow and every sample's raw-data
36
+ folder, through `APPTAINER_BIND`. Other folders the run needs (for example a `prerun`
37
+ embedding store elsewhere) still need a `--bind` in the profile's `apptainer-args`.
38
+
39
+ The command prints the Snakemake command it runs, and returns Snakemake's exit status.
40
+
41
+ ## `ophist init`
42
+
43
+ ```bash
44
+ ophist init DIR [--example NAME] [--force]
45
+ ```
46
+
47
+ Starts a run directory: it writes `DIR/config/config.yml` and `DIR/config/samples.tsv`.
48
+ - With `--example`, they are copied from one of the examples, together with the example's
49
+ `README.md`.
50
+ - Without it, they are the full default config and a template sample sheet.
51
+
52
+ Existing files are kept unless you pass `--force`.
53
+
54
+ ## `ophist examples`
55
+
56
+ Lists the example configs and what each is for. Their settings are explained in
57
+ [examples/README.md](../examples/README.md).
58
+
59
+ ## `ophist profile`
60
+
61
+ ```bash
62
+ ophist profile {slurm,local} [DEST] [--force]
63
+ ```
64
+
65
+ Copies a Snakemake profile template to `DEST` (default `profiles/<name>/`), to edit and then
66
+ pass to `ophist run --profile DEST`.
67
+ - `slurm` submits each job to SLURM. Set your account and partition, and the paths to bind,
68
+ in it. It needs `pip install ophist[slurm]`.
69
+ - `local` runs everything on one machine.
70
+
71
+ ## Without the command
72
+
73
+ The `ophist` command is a convenience. A clone of the repository runs with Snakemake alone, in
74
+ an environment that has Snakemake 9 and pandas:
75
+
76
+ ```bash
77
+ mamba create -n snakemake -c conda-forge -c bioconda snakemake pandas snakemake-executor-plugin-slurm
78
+ snakemake --snakefile /path/to/ophist/Snakefile -d my_run --profile ...
79
+ ```
80
+
81
+ Here `my_run/config/config.yml` is read automatically, over the defaults. The profile's
82
+ `apptainer-args` must then bind the clone and the data folders, since nothing sets
83
+ `APPTAINER_BIND` for you.