gset 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.
- gset-0.1.0/PKG-INFO +255 -0
- gset-0.1.0/README.md +239 -0
- gset-0.1.0/gset/__init__.py +11 -0
- gset-0.1.0/gset/api.py +109 -0
- gset-0.1.0/gset/cardamom_eval.py +56 -0
- gset-0.1.0/gset/clustering.py +62 -0
- gset-0.1.0/gset/data.py +64 -0
- gset-0.1.0/gset/embeddings.py +167 -0
- gset-0.1.0/gset/graph.py +50 -0
- gset-0.1.0/gset/lags.py +25 -0
- gset-0.1.0/gset/metrics.py +87 -0
- gset-0.1.0/gset/ot_similarity.py +73 -0
- gset-0.1.0/gset/pipeline.py +56 -0
- gset-0.1.0/gset/programs.py +107 -0
- gset-0.1.0/gset/reproducible.py +16 -0
- gset-0.1.0/gset/selection.py +97 -0
- gset-0.1.0/gset/signals.py +61 -0
- gset-0.1.0/gset.egg-info/PKG-INFO +255 -0
- gset-0.1.0/gset.egg-info/SOURCES.txt +24 -0
- gset-0.1.0/gset.egg-info/dependency_links.txt +1 -0
- gset-0.1.0/gset.egg-info/requires.txt +7 -0
- gset-0.1.0/gset.egg-info/top_level.txt +2 -0
- gset-0.1.0/pyproject.toml +21 -0
- gset-0.1.0/rmsexplorer/__init__.py +3 -0
- gset-0.1.0/rmsexplorer/encoder.py +115 -0
- gset-0.1.0/setup.cfg +4 -0
gset-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gset
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: GSET: Gene Signals from Embedding Trajectories select compact contexts for mechanistic GRN inference
|
|
5
|
+
Author: Zeev Weizmann, Elias Ventre
|
|
6
|
+
Project-URL: Repository, https://github.com/ZeevWeizmann/gset
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: numpy
|
|
10
|
+
Requires-Dist: scipy
|
|
11
|
+
Requires-Dist: pandas
|
|
12
|
+
Requires-Dist: scikit-learn
|
|
13
|
+
Requires-Dist: torch
|
|
14
|
+
Requires-Dist: torch_geometric
|
|
15
|
+
Requires-Dist: pot
|
|
16
|
+
|
|
17
|
+
# GSET: Gene Signals from Embedding Trajectories select compact contexts for mechanistic GRN inference
|
|
18
|
+
|
|
19
|
+
Zeev Weizmann and Elias Ventre
|
|
20
|
+
|
|
21
|
+
Code for the paper *GSET: Gene Signals from Embedding Trajectories select compact contexts for mechanistic GRN inference*. It contains the method (also an installable Python package, see "Use on your own data"),
|
|
22
|
+
the simulated data and the code that makes the figures and tables of the paper (see the list of what is
|
|
23
|
+
not included below).
|
|
24
|
+
|
|
25
|
+
## What is in the folders
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
gset/ the method (the installable package)
|
|
29
|
+
data.py reads the simulations
|
|
30
|
+
graph.py co-expression graph of the genes
|
|
31
|
+
embeddings.py GCN (co-expression) and DeepSets (expression) encoders of the simulations
|
|
32
|
+
signals.py KL signal and OT residual
|
|
33
|
+
lags.py lagged similarity matrices and the scores of the genes
|
|
34
|
+
programs.py finds the programs, their order and their core genes
|
|
35
|
+
selection.py builds the gene set (bridges + cores) and the baselines HVG, TF list, random
|
|
36
|
+
metrics.py coverage of relay edges and other scores
|
|
37
|
+
clustering.py scores for the recovery of programs
|
|
38
|
+
cardamom_eval.py scores a network inferred by CardamomOT
|
|
39
|
+
ot_similarity.py gene-gene OT distance (Huizing et al.)
|
|
40
|
+
pipeline.py trains the encoders and gives the vectors of all transitions
|
|
41
|
+
reproducible.py makes the neural networks give the same numbers in every run
|
|
42
|
+
rmsexplorer/ the co-expression encoder of the Gene Program Explorer, shared by the RMS and reprogramming experiments
|
|
43
|
+
baselines/ wrappers of methods from other authors (OTVelo, DELVE, lagged OT similarity)
|
|
44
|
+
data/simulations/ the 41 simulated networks (HARISSA) used in the paper
|
|
45
|
+
data/rms/ the rhabdomyosarcoma counts with the six pseudotime bins and the UMAP coordinates
|
|
46
|
+
data/schiebinger_days0-3.h5ad the prepared reprogramming data (days 0-3), unpacked by experiments/schiebinger/unpack_data.py
|
|
47
|
+
simulation_code/ the code that made the simulated networks (as it was used, see its README)
|
|
48
|
+
experiments/<block>/ computes the numbers of one block of the paper, saves them in results/<block>/
|
|
49
|
+
results/<block>/ saved numbers
|
|
50
|
+
figures/<block>/ make_figures.py and the pictures in output/
|
|
51
|
+
tables/<block>/ make_tables.py, prints the LaTeX of the tables
|
|
52
|
+
run_pipeline.py runs the blocks
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Where is what in the paper
|
|
56
|
+
|
|
57
|
+
Sections, appendices, tables and figures are numbered as in the submitted PDF of the paper
|
|
58
|
+
(`run_pipeline.py --list` prints the same references). `--all` runs the blocks in this order.
|
|
59
|
+
|
|
60
|
+
| Block (`run_pipeline.py --block ...`) | Paper |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `nb_signals` | Appendix B: negative binomial benchmark (Figs. 4 and 5) |
|
|
63
|
+
| `endpoint_signals` | Appendix C: endpoint maps (Figs. 6 and 7), Table 1, dropout figure (Fig. 8) |
|
|
64
|
+
| `program_recovery` | Section 3.3 and Appendix C: Table 3 (program recovery for all representations) |
|
|
65
|
+
| `lagged_association` | Section 3.4 and Appendix D: discrimination, direction, sampling resolution (Tables 4 to 6), lag figure (Fig. 9) |
|
|
66
|
+
| `rms` | Section 3.5 and Appendix L: query genes on the rhabdomyosarcoma data (Figs. 11 to 15, Tables 20 and 21) |
|
|
67
|
+
| `schiebinger` | Section 3.5 and Appendix K, the reprogramming time course (Schiebinger et al., days 0-3): Fig. 2, Table 19, the bridge genes between the programs of Lox and Birc5 (the query genes with `query_genes.py`; `bridges.py`, with the overlap of the bridges between training seeds against chance and the lagged-correlation baseline; the terms in which the bridges are enriched, which are not significant after correction, with `bridges_terms.py`; OTVelo as a baseline with `otvelo_baseline.py`) (needs the data, see below) |
|
|
68
|
+
| `retrieval_curves` | Section 3.6 and Appendix G: recall curves (Figs. 3 and 10) |
|
|
69
|
+
| `retrieval_selections` | Appendix F: ablation of the retrieval channels (Tables 9 and 10); the gene sets used by CardamomOT |
|
|
70
|
+
| `experiments/raw_embeddings/run.py` | Appendix F: Tables 11 and 12, GSET without the learned encoders (a script of its own, not a block of `run_pipeline.py`; the table is printed by `tables/raw_embeddings/make_tables.py`) |
|
|
71
|
+
| `experiments/spike_in/run.py`, `run_raw.py` | Appendix H: Table 15, 1000 real genes added to the 150-gene cascades, with and without the learned encoders (scripts of their own, results in `results/spike_in` and `results/spike_in_raw`; need `data/schiebinger`) |
|
|
72
|
+
| `baseline_selections` | Appendix E: the gene sets of DELVE, OTVelo and OT similarity (needs `DELVE_REPO` and `OTVELO_REPO`) |
|
|
73
|
+
| `extra_baselines` | Appendix E: Table 8 (temporal DE, GRNBoost2, GENIE3, WGCNA hubs, Hotspot; needs arboreto and hotspotsc, see `requirements.txt`) |
|
|
74
|
+
| `cardamom` | Section 3.6, Appendices E and G: CardamomOT on the retrieved contexts (Tables 2, 7, 13 and 14) (needs `CARDAMOM_REPO` to infer the networks again) |
|
|
75
|
+
| `scalability` | Appendix J: run times and memory (Tables 16 to 18) |
|
|
76
|
+
|
|
77
|
+
## What is not in the repository
|
|
78
|
+
|
|
79
|
+
* The whole reprogramming time course (165,892 cells, Schiebinger et al.; GEO: GSE122662) and the raw single-cell
|
|
80
|
+
RNA-seq counts of the rhabdomyosarcoma samples (GEO: GSE248183). Instead, the repository has the prepared days 0-3
|
|
81
|
+
of the reprogramming data (`data/schiebinger_days0-3.h5ad`) and the rhabdomyosarcoma counts with the pseudotime bins
|
|
82
|
+
(`data/rms/rms_counts.h5ad`). The code that makes these files from the full data is included:
|
|
83
|
+
`experiments/schiebinger/prepare_data.py` and `experiments/rms/make_pseudotime.py`.
|
|
84
|
+
* **Code that made the simulations** is in `simulation_code/` as it was used and was not run again for
|
|
85
|
+
this repository. The simulated networks themselves are in `data/simulations/`.
|
|
86
|
+
* **Baselines of other authors.** DELVE and OTVelo are not included (see Notes); the pipeline runs them
|
|
87
|
+
through the wrappers in `baselines/` when `DELVE_REPO` and `OTVELO_REPO` are set, and otherwise uses
|
|
88
|
+
the gene sets saved in `results/retrieval_selections/`. The networks inferred by CardamomOT are saved
|
|
89
|
+
in `results/cardamom_networks/`, and the tables are made from them.
|
|
90
|
+
* The overview figure of the paper is a drawing and is not made by code.
|
|
91
|
+
|
|
92
|
+
## Installation
|
|
93
|
+
|
|
94
|
+
Tested with Python 3.13.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install -r requirements.txt
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This is enough to rebuild every figure and table from the saved results
|
|
101
|
+
(`python run_pipeline.py --all --from-results`) and to run GSET itself.
|
|
102
|
+
|
|
103
|
+
To use the method in your own code, install the package `gset` too:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install -e .
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Methods of other authors (only to run them again)
|
|
110
|
+
|
|
111
|
+
The results of these methods are saved in `results/`. To compute them again, install the method
|
|
112
|
+
and set the variable; without the variable, `run_pipeline.py` skips the step and uses the saved
|
|
113
|
+
results. The versions below are the ones checked to give the results of the paper.
|
|
114
|
+
|
|
115
|
+
**CardamomOT** (block `cardamom`):
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
git clone https://github.com/eliasventre/CardamomOT
|
|
119
|
+
cd CardamomOT && git checkout 36c5ca2
|
|
120
|
+
pip install -e . && cd ..
|
|
121
|
+
export CARDAMOM_REPO=$PWD/CardamomOT
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Only on macOS with Apple silicon: if the process aborts when torch is loaded, apply
|
|
125
|
+
`experiments/cardamom/cardamomot_serial_numba.patch` before `pip install -e .` (`git apply /path/to/this/repository/experiments/cardamom/cardamomot_serial_numba.patch`).
|
|
126
|
+
It makes two numba loops serial instead of parallel. The networks of the paper were inferred with it.
|
|
127
|
+
|
|
128
|
+
**OTVelo** (block `baseline_selections`; GPL-3.0, so it is not copied here):
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
git clone https://github.com/Sandstede-Lab/OT-Velocity
|
|
132
|
+
cd OT-Velocity && git checkout 8b8df25 && cd ..
|
|
133
|
+
export OTVELO_REPO=$PWD/OT-Velocity/Utils
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**DELVE** (block `baseline_selections`). DELVE is used from its folder and not installed with pip,
|
|
137
|
+
because its package asks for numpy < 2:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone https://github.com/jranek/delve
|
|
141
|
+
cd delve && git checkout ff08435 && cd ..
|
|
142
|
+
export DELVE_REPO=$PWD/delve
|
|
143
|
+
pip install --no-deps sketchKH==0.1.4 tqdm_joblib==0.0.5
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**GRNBoost2, GENIE3 and Hotspot** (block `extra_baselines`):
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pip install --no-deps arboreto==0.1.6 hotspotsc==1.1.3
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Run
|
|
153
|
+
|
|
154
|
+
A quick check that everything works (about ten seconds): it makes Table 1 and Figs. 6 to 8 from the saved results.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
python run_pipeline.py --block endpoint_signals --from-results
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The other blocks:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
python run_pipeline.py --list # the blocks
|
|
164
|
+
python run_pipeline.py --block endpoint_signals # one block: experiment, figures, tables
|
|
165
|
+
python run_pipeline.py --all --from-results # only the figures and tables, from the saved results
|
|
166
|
+
python run_pipeline.py --all # everything again (many hours)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Every experiment can also be run alone, for example
|
|
170
|
+
`python experiments/program_recovery/run.py twophase60_s1 1`.
|
|
171
|
+
|
|
172
|
+
The pseudotime bins of the rhabdomyosarcoma data can be made again from the raw single-cell RNA-seq counts
|
|
173
|
+
(10x Genomics format). Set the folders of the two samples in `experiments/rms/make_pseudotime.py` and run
|
|
174
|
+
(about five minutes):
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
pip install palantir leidenalg igraph
|
|
178
|
+
python experiments/rms/make_pseudotime.py
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Use on your own data
|
|
182
|
+
|
|
183
|
+
Install the package from the folder of this repository:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
pip install -e .
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Then:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
import anndata as ad
|
|
193
|
+
from gset import select_genes
|
|
194
|
+
|
|
195
|
+
adata = ad.read_h5ad("my_data.h5ad") # raw counts in X, the time of every cell in adata.obs["time"]
|
|
196
|
+
genes = adata.var_names[select_genes(adata, budget=30, n_programs=3)]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`select_genes` trains the encoders on your data (see Notes for the two co-expression encoders) and returns the
|
|
200
|
+
indices of the selected genes: bridges between consecutive gene programs and the core
|
|
201
|
+
genes of the programs. By default a third of the budget goes to the cores and two thirds to the bridges, and the
|
|
202
|
+
programs are found by clustering, with their number given. Other settings:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
# the numbers of cores and bridges (the budget is their sum), and of the two channels of the bridges
|
|
206
|
+
genes = select_genes(adata, n_cores=10, n_bridges=20, n_kl=14, n_ot=6, n_programs=3)
|
|
207
|
+
|
|
208
|
+
# programs built around one query gene each, in the order in which the programs become active.
|
|
209
|
+
# The program of a query gene is the gene and its program_size - 1 nearest genes, here 10 genes in all.
|
|
210
|
+
queries = [adata.var_names.get_loc("Lox"), adata.var_names.get_loc("Birc5")]
|
|
211
|
+
genes = select_genes(adata, budget=30, query_genes=queries, program_size=10)
|
|
212
|
+
|
|
213
|
+
# programs of your own: the program of every gene (0, 1, 2, ...) and the order of the programs
|
|
214
|
+
genes = select_genes(adata, budget=30, labels=labels, order=[0, 1, 2])
|
|
215
|
+
|
|
216
|
+
# the co-expression encoder of the experiments on the real data, for thousands of genes
|
|
217
|
+
genes = select_genes(adata, budget=30, n_programs=3, coexpr_encoder="real_data")
|
|
218
|
+
|
|
219
|
+
# the data as a list of count matrices (cells x genes), one per timepoint, instead of an AnnData object
|
|
220
|
+
genes = select_genes([X_t0, X_t1, X_t2], budget=30, n_programs=3)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
With several replicates, pass a list of such lists. The genes can then be given to CardamomOT or another network
|
|
224
|
+
inference method. The genes related to a query gene can also be explored interactively in the Gene Program Explorer
|
|
225
|
+
(https://rmsexplorer.org).
|
|
226
|
+
|
|
227
|
+
## Notes
|
|
228
|
+
|
|
229
|
+
* **Same numbers on every run.** Every experiment calls `make_reproducible()`: torch uses one
|
|
230
|
+
thread and deterministic algorithms. Without it, the sums of torch change in the last digits
|
|
231
|
+
when it uses several threads, and this can change which genes are ranked first.
|
|
232
|
+
* **Encoders.** The co-expression embedding, which gives the KL signal, has two encoders. The one in
|
|
233
|
+
`gset/embeddings.py` is used on the simulations, which have tens to a few hundred genes, and is the default of
|
|
234
|
+
`select_genes` (`coexpr_encoder="simulation"`). The one in `rmsexplorer/encoder.py` is used on the real data
|
|
235
|
+
(rhabdomyosarcoma, reprogramming), with thousands of genes (`coexpr_encoder="real_data"`), see `experiments/rms` and
|
|
236
|
+
`experiments/schiebinger`.
|
|
237
|
+
* **Methods of other authors** (CardamomOT, OTVelo, DELVE, arboreto, Hotspot) are not part of this
|
|
238
|
+
repository; see Installation.
|
|
239
|
+
* **Data.** The four data sets of the paper:
|
|
240
|
+
* *Negative binomial mixtures.* They are not stored: `experiments/nb_signals/nb_data.py` simulates them (40 genes,
|
|
241
|
+
5,000 cells per timepoint, two scenarios, rewiring and positional deviation, at several dropout rates). The scores are
|
|
242
|
+
in `results/nb_signals/`.
|
|
243
|
+
* *HARISSA simulations* (https://github.com/ulysseherbach/harissa). Cascades, switches and the families with
|
|
244
|
+
TF-to-TF relays, 41 networks in `data/simulations/`, made by `simulation_code/`.
|
|
245
|
+
* *Reprogramming time course* of Schiebinger et al. (Cell, 2019; GEO: GSE122662, serum condition), days 0-3,
|
|
246
|
+
34,101 cells and 4,007 genes, in `data/schiebinger_days0-3.h5ad`. Run
|
|
247
|
+
`python experiments/schiebinger/unpack_data.py` once to unpack it into `data/schiebinger/`, which the block
|
|
248
|
+
`schiebinger` and `experiments/spike_in/` use. The same files can be made from the original serum data, saved as
|
|
249
|
+
`data/schiebinger_raw/reprogramming_schiebinger_only_serum.h5ad`, with `experiments/schiebinger/prepare_data.py`.
|
|
250
|
+
`query_genes.py` also needs Table S2 of the paper (`mmc2.xlsx`), saved as
|
|
251
|
+
`data/schiebinger_raw/schiebinger2019_mmc2.xlsx`.
|
|
252
|
+
* *Rhabdomyosarcoma (RMS)* of Savary et al. (Cell Reports Medicine, 2023; GEO: GSE248183), 13,968 cells and 8,442
|
|
253
|
+
genes, with the six pseudotime bins (the column `time`), the cell types (`cell_type`) and the UMAP coordinates, in
|
|
254
|
+
`data/rms/`. All the analyses use this file. `experiments/rms/make_pseudotime.py` makes a similar file from the raw
|
|
255
|
+
single-cell RNA-seq counts (see Run). It needs `palantir`, `leidenalg` and `igraph`.
|
gset-0.1.0/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# GSET: Gene Signals from Embedding Trajectories select compact contexts for mechanistic GRN inference
|
|
2
|
+
|
|
3
|
+
Zeev Weizmann and Elias Ventre
|
|
4
|
+
|
|
5
|
+
Code for the paper *GSET: Gene Signals from Embedding Trajectories select compact contexts for mechanistic GRN inference*. It contains the method (also an installable Python package, see "Use on your own data"),
|
|
6
|
+
the simulated data and the code that makes the figures and tables of the paper (see the list of what is
|
|
7
|
+
not included below).
|
|
8
|
+
|
|
9
|
+
## What is in the folders
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
gset/ the method (the installable package)
|
|
13
|
+
data.py reads the simulations
|
|
14
|
+
graph.py co-expression graph of the genes
|
|
15
|
+
embeddings.py GCN (co-expression) and DeepSets (expression) encoders of the simulations
|
|
16
|
+
signals.py KL signal and OT residual
|
|
17
|
+
lags.py lagged similarity matrices and the scores of the genes
|
|
18
|
+
programs.py finds the programs, their order and their core genes
|
|
19
|
+
selection.py builds the gene set (bridges + cores) and the baselines HVG, TF list, random
|
|
20
|
+
metrics.py coverage of relay edges and other scores
|
|
21
|
+
clustering.py scores for the recovery of programs
|
|
22
|
+
cardamom_eval.py scores a network inferred by CardamomOT
|
|
23
|
+
ot_similarity.py gene-gene OT distance (Huizing et al.)
|
|
24
|
+
pipeline.py trains the encoders and gives the vectors of all transitions
|
|
25
|
+
reproducible.py makes the neural networks give the same numbers in every run
|
|
26
|
+
rmsexplorer/ the co-expression encoder of the Gene Program Explorer, shared by the RMS and reprogramming experiments
|
|
27
|
+
baselines/ wrappers of methods from other authors (OTVelo, DELVE, lagged OT similarity)
|
|
28
|
+
data/simulations/ the 41 simulated networks (HARISSA) used in the paper
|
|
29
|
+
data/rms/ the rhabdomyosarcoma counts with the six pseudotime bins and the UMAP coordinates
|
|
30
|
+
data/schiebinger_days0-3.h5ad the prepared reprogramming data (days 0-3), unpacked by experiments/schiebinger/unpack_data.py
|
|
31
|
+
simulation_code/ the code that made the simulated networks (as it was used, see its README)
|
|
32
|
+
experiments/<block>/ computes the numbers of one block of the paper, saves them in results/<block>/
|
|
33
|
+
results/<block>/ saved numbers
|
|
34
|
+
figures/<block>/ make_figures.py and the pictures in output/
|
|
35
|
+
tables/<block>/ make_tables.py, prints the LaTeX of the tables
|
|
36
|
+
run_pipeline.py runs the blocks
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Where is what in the paper
|
|
40
|
+
|
|
41
|
+
Sections, appendices, tables and figures are numbered as in the submitted PDF of the paper
|
|
42
|
+
(`run_pipeline.py --list` prints the same references). `--all` runs the blocks in this order.
|
|
43
|
+
|
|
44
|
+
| Block (`run_pipeline.py --block ...`) | Paper |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `nb_signals` | Appendix B: negative binomial benchmark (Figs. 4 and 5) |
|
|
47
|
+
| `endpoint_signals` | Appendix C: endpoint maps (Figs. 6 and 7), Table 1, dropout figure (Fig. 8) |
|
|
48
|
+
| `program_recovery` | Section 3.3 and Appendix C: Table 3 (program recovery for all representations) |
|
|
49
|
+
| `lagged_association` | Section 3.4 and Appendix D: discrimination, direction, sampling resolution (Tables 4 to 6), lag figure (Fig. 9) |
|
|
50
|
+
| `rms` | Section 3.5 and Appendix L: query genes on the rhabdomyosarcoma data (Figs. 11 to 15, Tables 20 and 21) |
|
|
51
|
+
| `schiebinger` | Section 3.5 and Appendix K, the reprogramming time course (Schiebinger et al., days 0-3): Fig. 2, Table 19, the bridge genes between the programs of Lox and Birc5 (the query genes with `query_genes.py`; `bridges.py`, with the overlap of the bridges between training seeds against chance and the lagged-correlation baseline; the terms in which the bridges are enriched, which are not significant after correction, with `bridges_terms.py`; OTVelo as a baseline with `otvelo_baseline.py`) (needs the data, see below) |
|
|
52
|
+
| `retrieval_curves` | Section 3.6 and Appendix G: recall curves (Figs. 3 and 10) |
|
|
53
|
+
| `retrieval_selections` | Appendix F: ablation of the retrieval channels (Tables 9 and 10); the gene sets used by CardamomOT |
|
|
54
|
+
| `experiments/raw_embeddings/run.py` | Appendix F: Tables 11 and 12, GSET without the learned encoders (a script of its own, not a block of `run_pipeline.py`; the table is printed by `tables/raw_embeddings/make_tables.py`) |
|
|
55
|
+
| `experiments/spike_in/run.py`, `run_raw.py` | Appendix H: Table 15, 1000 real genes added to the 150-gene cascades, with and without the learned encoders (scripts of their own, results in `results/spike_in` and `results/spike_in_raw`; need `data/schiebinger`) |
|
|
56
|
+
| `baseline_selections` | Appendix E: the gene sets of DELVE, OTVelo and OT similarity (needs `DELVE_REPO` and `OTVELO_REPO`) |
|
|
57
|
+
| `extra_baselines` | Appendix E: Table 8 (temporal DE, GRNBoost2, GENIE3, WGCNA hubs, Hotspot; needs arboreto and hotspotsc, see `requirements.txt`) |
|
|
58
|
+
| `cardamom` | Section 3.6, Appendices E and G: CardamomOT on the retrieved contexts (Tables 2, 7, 13 and 14) (needs `CARDAMOM_REPO` to infer the networks again) |
|
|
59
|
+
| `scalability` | Appendix J: run times and memory (Tables 16 to 18) |
|
|
60
|
+
|
|
61
|
+
## What is not in the repository
|
|
62
|
+
|
|
63
|
+
* The whole reprogramming time course (165,892 cells, Schiebinger et al.; GEO: GSE122662) and the raw single-cell
|
|
64
|
+
RNA-seq counts of the rhabdomyosarcoma samples (GEO: GSE248183). Instead, the repository has the prepared days 0-3
|
|
65
|
+
of the reprogramming data (`data/schiebinger_days0-3.h5ad`) and the rhabdomyosarcoma counts with the pseudotime bins
|
|
66
|
+
(`data/rms/rms_counts.h5ad`). The code that makes these files from the full data is included:
|
|
67
|
+
`experiments/schiebinger/prepare_data.py` and `experiments/rms/make_pseudotime.py`.
|
|
68
|
+
* **Code that made the simulations** is in `simulation_code/` as it was used and was not run again for
|
|
69
|
+
this repository. The simulated networks themselves are in `data/simulations/`.
|
|
70
|
+
* **Baselines of other authors.** DELVE and OTVelo are not included (see Notes); the pipeline runs them
|
|
71
|
+
through the wrappers in `baselines/` when `DELVE_REPO` and `OTVELO_REPO` are set, and otherwise uses
|
|
72
|
+
the gene sets saved in `results/retrieval_selections/`. The networks inferred by CardamomOT are saved
|
|
73
|
+
in `results/cardamom_networks/`, and the tables are made from them.
|
|
74
|
+
* The overview figure of the paper is a drawing and is not made by code.
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
Tested with Python 3.13.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install -r requirements.txt
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This is enough to rebuild every figure and table from the saved results
|
|
85
|
+
(`python run_pipeline.py --all --from-results`) and to run GSET itself.
|
|
86
|
+
|
|
87
|
+
To use the method in your own code, install the package `gset` too:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install -e .
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Methods of other authors (only to run them again)
|
|
94
|
+
|
|
95
|
+
The results of these methods are saved in `results/`. To compute them again, install the method
|
|
96
|
+
and set the variable; without the variable, `run_pipeline.py` skips the step and uses the saved
|
|
97
|
+
results. The versions below are the ones checked to give the results of the paper.
|
|
98
|
+
|
|
99
|
+
**CardamomOT** (block `cardamom`):
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
git clone https://github.com/eliasventre/CardamomOT
|
|
103
|
+
cd CardamomOT && git checkout 36c5ca2
|
|
104
|
+
pip install -e . && cd ..
|
|
105
|
+
export CARDAMOM_REPO=$PWD/CardamomOT
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Only on macOS with Apple silicon: if the process aborts when torch is loaded, apply
|
|
109
|
+
`experiments/cardamom/cardamomot_serial_numba.patch` before `pip install -e .` (`git apply /path/to/this/repository/experiments/cardamom/cardamomot_serial_numba.patch`).
|
|
110
|
+
It makes two numba loops serial instead of parallel. The networks of the paper were inferred with it.
|
|
111
|
+
|
|
112
|
+
**OTVelo** (block `baseline_selections`; GPL-3.0, so it is not copied here):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone https://github.com/Sandstede-Lab/OT-Velocity
|
|
116
|
+
cd OT-Velocity && git checkout 8b8df25 && cd ..
|
|
117
|
+
export OTVELO_REPO=$PWD/OT-Velocity/Utils
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**DELVE** (block `baseline_selections`). DELVE is used from its folder and not installed with pip,
|
|
121
|
+
because its package asks for numpy < 2:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
git clone https://github.com/jranek/delve
|
|
125
|
+
cd delve && git checkout ff08435 && cd ..
|
|
126
|
+
export DELVE_REPO=$PWD/delve
|
|
127
|
+
pip install --no-deps sketchKH==0.1.4 tqdm_joblib==0.0.5
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**GRNBoost2, GENIE3 and Hotspot** (block `extra_baselines`):
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pip install --no-deps arboreto==0.1.6 hotspotsc==1.1.3
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Run
|
|
137
|
+
|
|
138
|
+
A quick check that everything works (about ten seconds): it makes Table 1 and Figs. 6 to 8 from the saved results.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
python run_pipeline.py --block endpoint_signals --from-results
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The other blocks:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
python run_pipeline.py --list # the blocks
|
|
148
|
+
python run_pipeline.py --block endpoint_signals # one block: experiment, figures, tables
|
|
149
|
+
python run_pipeline.py --all --from-results # only the figures and tables, from the saved results
|
|
150
|
+
python run_pipeline.py --all # everything again (many hours)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Every experiment can also be run alone, for example
|
|
154
|
+
`python experiments/program_recovery/run.py twophase60_s1 1`.
|
|
155
|
+
|
|
156
|
+
The pseudotime bins of the rhabdomyosarcoma data can be made again from the raw single-cell RNA-seq counts
|
|
157
|
+
(10x Genomics format). Set the folders of the two samples in `experiments/rms/make_pseudotime.py` and run
|
|
158
|
+
(about five minutes):
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
pip install palantir leidenalg igraph
|
|
162
|
+
python experiments/rms/make_pseudotime.py
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Use on your own data
|
|
166
|
+
|
|
167
|
+
Install the package from the folder of this repository:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
pip install -e .
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Then:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
import anndata as ad
|
|
177
|
+
from gset import select_genes
|
|
178
|
+
|
|
179
|
+
adata = ad.read_h5ad("my_data.h5ad") # raw counts in X, the time of every cell in adata.obs["time"]
|
|
180
|
+
genes = adata.var_names[select_genes(adata, budget=30, n_programs=3)]
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`select_genes` trains the encoders on your data (see Notes for the two co-expression encoders) and returns the
|
|
184
|
+
indices of the selected genes: bridges between consecutive gene programs and the core
|
|
185
|
+
genes of the programs. By default a third of the budget goes to the cores and two thirds to the bridges, and the
|
|
186
|
+
programs are found by clustering, with their number given. Other settings:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
# the numbers of cores and bridges (the budget is their sum), and of the two channels of the bridges
|
|
190
|
+
genes = select_genes(adata, n_cores=10, n_bridges=20, n_kl=14, n_ot=6, n_programs=3)
|
|
191
|
+
|
|
192
|
+
# programs built around one query gene each, in the order in which the programs become active.
|
|
193
|
+
# The program of a query gene is the gene and its program_size - 1 nearest genes, here 10 genes in all.
|
|
194
|
+
queries = [adata.var_names.get_loc("Lox"), adata.var_names.get_loc("Birc5")]
|
|
195
|
+
genes = select_genes(adata, budget=30, query_genes=queries, program_size=10)
|
|
196
|
+
|
|
197
|
+
# programs of your own: the program of every gene (0, 1, 2, ...) and the order of the programs
|
|
198
|
+
genes = select_genes(adata, budget=30, labels=labels, order=[0, 1, 2])
|
|
199
|
+
|
|
200
|
+
# the co-expression encoder of the experiments on the real data, for thousands of genes
|
|
201
|
+
genes = select_genes(adata, budget=30, n_programs=3, coexpr_encoder="real_data")
|
|
202
|
+
|
|
203
|
+
# the data as a list of count matrices (cells x genes), one per timepoint, instead of an AnnData object
|
|
204
|
+
genes = select_genes([X_t0, X_t1, X_t2], budget=30, n_programs=3)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
With several replicates, pass a list of such lists. The genes can then be given to CardamomOT or another network
|
|
208
|
+
inference method. The genes related to a query gene can also be explored interactively in the Gene Program Explorer
|
|
209
|
+
(https://rmsexplorer.org).
|
|
210
|
+
|
|
211
|
+
## Notes
|
|
212
|
+
|
|
213
|
+
* **Same numbers on every run.** Every experiment calls `make_reproducible()`: torch uses one
|
|
214
|
+
thread and deterministic algorithms. Without it, the sums of torch change in the last digits
|
|
215
|
+
when it uses several threads, and this can change which genes are ranked first.
|
|
216
|
+
* **Encoders.** The co-expression embedding, which gives the KL signal, has two encoders. The one in
|
|
217
|
+
`gset/embeddings.py` is used on the simulations, which have tens to a few hundred genes, and is the default of
|
|
218
|
+
`select_genes` (`coexpr_encoder="simulation"`). The one in `rmsexplorer/encoder.py` is used on the real data
|
|
219
|
+
(rhabdomyosarcoma, reprogramming), with thousands of genes (`coexpr_encoder="real_data"`), see `experiments/rms` and
|
|
220
|
+
`experiments/schiebinger`.
|
|
221
|
+
* **Methods of other authors** (CardamomOT, OTVelo, DELVE, arboreto, Hotspot) are not part of this
|
|
222
|
+
repository; see Installation.
|
|
223
|
+
* **Data.** The four data sets of the paper:
|
|
224
|
+
* *Negative binomial mixtures.* They are not stored: `experiments/nb_signals/nb_data.py` simulates them (40 genes,
|
|
225
|
+
5,000 cells per timepoint, two scenarios, rewiring and positional deviation, at several dropout rates). The scores are
|
|
226
|
+
in `results/nb_signals/`.
|
|
227
|
+
* *HARISSA simulations* (https://github.com/ulysseherbach/harissa). Cascades, switches and the families with
|
|
228
|
+
TF-to-TF relays, 41 networks in `data/simulations/`, made by `simulation_code/`.
|
|
229
|
+
* *Reprogramming time course* of Schiebinger et al. (Cell, 2019; GEO: GSE122662, serum condition), days 0-3,
|
|
230
|
+
34,101 cells and 4,007 genes, in `data/schiebinger_days0-3.h5ad`. Run
|
|
231
|
+
`python experiments/schiebinger/unpack_data.py` once to unpack it into `data/schiebinger/`, which the block
|
|
232
|
+
`schiebinger` and `experiments/spike_in/` use. The same files can be made from the original serum data, saved as
|
|
233
|
+
`data/schiebinger_raw/reprogramming_schiebinger_only_serum.h5ad`, with `experiments/schiebinger/prepare_data.py`.
|
|
234
|
+
`query_genes.py` also needs Table S2 of the paper (`mmc2.xlsx`), saved as
|
|
235
|
+
`data/schiebinger_raw/schiebinger2019_mmc2.xlsx`.
|
|
236
|
+
* *Rhabdomyosarcoma (RMS)* of Savary et al. (Cell Reports Medicine, 2023; GEO: GSE248183), 13,968 cells and 8,442
|
|
237
|
+
genes, with the six pseudotime bins (the column `time`), the cell types (`cell_type`) and the UMAP coordinates, in
|
|
238
|
+
`data/rms/`. All the analyses use this file. `experiments/rms/make_pseudotime.py` makes a similar file from the raw
|
|
239
|
+
single-cell RNA-seq counts (see Run). It needs `palantir`, `leidenalg` and `igraph`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .graph import build_graph
|
|
2
|
+
from .embeddings import (train_gcn, coexpr_embedding,
|
|
3
|
+
train_deepsets, expr_embedding, train_both)
|
|
4
|
+
from .signals import kl_vector, kl_score, ot_residual, ot_score
|
|
5
|
+
from .lags import lag_matrix, symmetric_score
|
|
6
|
+
from .programs import find_programs, program_order, core_genes, programs_from_queries
|
|
7
|
+
from .selection import gset_context, bridge_genes, hvg_ranking, tf_selection, random_selection
|
|
8
|
+
from .metrics import relay_coverage, within_between_gap
|
|
9
|
+
from .pipeline import transition_vectors
|
|
10
|
+
from .reproducible import make_reproducible
|
|
11
|
+
from .api import select_genes, matrices_from_anndata
|
gset-0.1.0/gset/api.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""select_genes: GSET in one function.
|
|
2
|
+
|
|
3
|
+
from gset import select_genes
|
|
4
|
+
genes = select_genes(adata, budget=30, n_programs=3)
|
|
5
|
+
|
|
6
|
+
It follows the steps of the paper:
|
|
7
|
+
1. train the encoders and compute the KL and OT signals of every transition
|
|
8
|
+
2. find the gene programs, and their order
|
|
9
|
+
3. choose the bridge genes between consecutive programs and the core genes of the programs
|
|
10
|
+
"""
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from .lags import lag_matrix
|
|
14
|
+
from .pipeline import transition_vectors
|
|
15
|
+
from .programs import concat_embedding, find_programs, mean_profiles, program_order, programs_from_queries
|
|
16
|
+
from .selection import gset_context
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def matrices_from_anndata(adata, time_key="time"):
|
|
20
|
+
"""The count matrices (cells x genes), one per timepoint, in the order of the time.
|
|
21
|
+
The counts are in adata.X and the time of every cell is in adata.obs[time_key]."""
|
|
22
|
+
X = adata.X.toarray() if hasattr(adata.X, "toarray") else np.asarray(adata.X)
|
|
23
|
+
times = np.asarray(adata.obs[time_key])
|
|
24
|
+
return [X[times == t].astype(float) for t in np.unique(times)]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _runs_from(data, time_key):
|
|
28
|
+
"""Every run is a dict {timepoint: matrix}. Here: one run, or one for every replicate."""
|
|
29
|
+
if hasattr(data, "obs"): # an AnnData object
|
|
30
|
+
data = matrices_from_anndata(data, time_key)
|
|
31
|
+
if isinstance(data[0], np.ndarray): # one run: a list of matrices
|
|
32
|
+
data = [data]
|
|
33
|
+
runs = [dict(enumerate(run)) for run in data]
|
|
34
|
+
return runs, list(range(len(runs[0])))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _split_budget(budget, n_cores, n_bridges, n_kl, n_ot):
|
|
38
|
+
"""How many genes are cores, bridges from the KL signal and bridges from the OT signal."""
|
|
39
|
+
if n_cores is None and n_bridges is None:
|
|
40
|
+
n_cores = budget // 3 # a third of the budget for the cores
|
|
41
|
+
n_bridges = budget - n_cores # two thirds for the bridges
|
|
42
|
+
elif n_cores is None:
|
|
43
|
+
n_cores = budget - n_bridges
|
|
44
|
+
elif n_bridges is None:
|
|
45
|
+
n_bridges = budget - n_cores
|
|
46
|
+
if n_kl is None and n_ot is None:
|
|
47
|
+
n_ot = int(round(0.3 * n_bridges)) # 70% of the bridges from KL, 30% from OT
|
|
48
|
+
if n_kl is None:
|
|
49
|
+
n_kl = n_bridges - n_ot
|
|
50
|
+
if n_ot is None:
|
|
51
|
+
n_ot = n_bridges - n_kl
|
|
52
|
+
return n_cores + n_bridges, n_kl, n_ot
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def select_genes(data, budget=30, n_programs=3, n_cores=None, n_bridges=None, n_kl=None, n_ot=None,
|
|
56
|
+
query_genes=None, program_size=30, labels=None, order=None,
|
|
57
|
+
coexpr_encoder="simulation", time_key="time", seed=42, n_epochs=None):
|
|
58
|
+
"""Select a compact set of genes from time-resolved single-cell data.
|
|
59
|
+
|
|
60
|
+
Returns the sorted list of the indices of the selected genes: the bridge genes between consecutive
|
|
61
|
+
programs and the core genes of the programs.
|
|
62
|
+
|
|
63
|
+
Data
|
|
64
|
+
data an AnnData object (raw counts in X, the time of every cell in obs[time_key]) or a list of
|
|
65
|
+
count matrices (cells x genes), one per timepoint in the order of the time. With several
|
|
66
|
+
replicates, a list of such lists. The cells of different timepoints do not have to be matched.
|
|
67
|
+
time_key the column of obs with the time of the cells ("time")
|
|
68
|
+
|
|
69
|
+
Number of genes
|
|
70
|
+
budget how many genes to select (30)
|
|
71
|
+
n_cores how many of them are core genes (default: a third of the budget)
|
|
72
|
+
n_bridges how many are bridges (default: the rest). If you give both n_cores and n_bridges, the budget
|
|
73
|
+
is their sum.
|
|
74
|
+
n_kl, n_ot how many of the bridges come from the KL signal without lag and from the OT signal with lag 1
|
|
75
|
+
(default: 70% and 30%)
|
|
76
|
+
|
|
77
|
+
Programs (the groups of genes that work together). Choose one of the three ways:
|
|
78
|
+
n_programs the number of programs, found by clustering the genes (3)
|
|
79
|
+
query_genes the indices of one gene for every program, in the order in which the programs become active.
|
|
80
|
+
The program of a query gene is the gene and its program_size - 1 nearest genes (program_size
|
|
81
|
+
is 30), and the other genes go to the program with the nearest centroid.
|
|
82
|
+
labels, order the program of every gene (0, 1, 2, ...) and the order of the programs (by default 0, 1, 2, ...)
|
|
83
|
+
|
|
84
|
+
Encoders
|
|
85
|
+
coexpr_encoder the encoder of the co-expression embedding (the KL signal). "simulation" (default): the
|
|
86
|
+
encoder of the simulations, for panels of tens to a few hundred genes. "real_data": the encoder
|
|
87
|
+
of the experiments on the real data, for thousands of genes (rmsexplorer/encoder.py).
|
|
88
|
+
seed, n_epochs the training. n_epochs: 300 for "simulation", 200 and 150 for "real_data" (default).
|
|
89
|
+
"""
|
|
90
|
+
runs, times = _runs_from(data, time_key)
|
|
91
|
+
|
|
92
|
+
# 1. the signals of every transition
|
|
93
|
+
vectors = transition_vectors(runs, times, seed=seed, n_epochs=n_epochs, coexpr_encoder=coexpr_encoder)
|
|
94
|
+
kl_lag0 = lag_matrix(vectors["kl"], 0)
|
|
95
|
+
ot_lag1 = lag_matrix(vectors["ot"], 1)
|
|
96
|
+
z_concat = concat_embedding(vectors["gcn"], runs, vectors["active"], embed=vectors["embed"])
|
|
97
|
+
|
|
98
|
+
# 2. the programs and their order
|
|
99
|
+
if query_genes is not None:
|
|
100
|
+
labels, order = programs_from_queries(z_concat, query_genes, program_size)
|
|
101
|
+
elif labels is None:
|
|
102
|
+
labels = find_programs(z_concat, n_programs)
|
|
103
|
+
order, _ = program_order(labels, mean_profiles(runs, vectors["active"]))
|
|
104
|
+
elif order is None:
|
|
105
|
+
order = list(range(int(np.max(labels)) + 1))
|
|
106
|
+
|
|
107
|
+
# 3. the bridges and the cores
|
|
108
|
+
budget, n_kl, n_ot = _split_budget(budget, n_cores, n_bridges, n_kl, n_ot)
|
|
109
|
+
return gset_context(budget, n_kl, n_ot, np.asarray(labels), order, kl_lag0, ot_lag1, z_concat)
|