gpath2vec 3.0.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Nasim Sanati
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,342 @@
1
+ Metadata-Version: 2.4
2
+ Name: gpath2vec
3
+ Version: 3.0.0
4
+ Summary: gene-set to biological pathway embeddings with enrichment analysis
5
+ Home-page: https://github.com/teslajoy/gpath2vec
6
+ Author: Nasim Sanati
7
+ Author-email: nasim@plenary.org
8
+ License: MIT
9
+ Project-URL: Bug Tracker, https://github.com/teslajoy/gpath2vec/issues
10
+ Project-URL: Source, https://github.com/teslajoy/gpath2vec
11
+ Keywords: bioinformatics pathways embeddings enrichment-analysis metapath2vec reactome
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: torch
23
+ Requires-Dist: networkx
24
+ Requires-Dist: requests
25
+ Requires-Dist: click
26
+ Requires-Dist: numpy
27
+ Requires-Dist: pandas
28
+ Requires-Dist: scipy
29
+ Requires-Dist: statsmodels
30
+ Requires-Dist: scikit-learn
31
+ Provides-Extra: test
32
+ Requires-Dist: pytest; extra == "test"
33
+ Provides-Extra: aucell
34
+ Requires-Dist: decoupler>=2.1; extra == "aucell"
35
+ Requires-Dist: anndata; extra == "aucell"
36
+ Dynamic: author
37
+ Dynamic: author-email
38
+ Dynamic: classifier
39
+ Dynamic: description
40
+ Dynamic: description-content-type
41
+ Dynamic: home-page
42
+ Dynamic: keywords
43
+ Dynamic: license
44
+ Dynamic: license-file
45
+ Dynamic: project-url
46
+ Dynamic: provides-extra
47
+ Dynamic: requires-dist
48
+ Dynamic: requires-python
49
+ Dynamic: summary
50
+
51
+ # gpath2vec
52
+
53
+ ![Status](https://img.shields.io/badge/Status-Build%20Passing-lgreen)
54
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
55
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22681141.svg)](https://doi.org/10.5281/zenodo.22681141)
56
+
57
+ a python package for converting gene sets to biological pathway embeddings with enrichment analysis attributes.
58
+
59
+ gene sets (from clusters, niches, studies) are tested against reactome pathways via fisher's exact test, then embedded into a shared vector space using metapath2vec over the pathway hierarchy graph.
60
+
61
+ enrichment is either fisher's exact (a binary top-N gene set per cluster/niche) or **niche-level AUCell** (the full per-niche expression ranking, no gene-set selection step). AUCell here scores each niche's **aggregated pseudobulk** profile, one score per (niche, pathway); it is **not single-cell AUCell** (the package never sees individual cells, niche construction is upstream). both enrichment sources feed the same reactome hierarchy graph and metapath2vec embedding.
62
+
63
+ ![gpath2vec](https://raw.githubusercontent.com/teslajoy/gpath2vec/main/img/gpath2vec.png)
64
+
65
+ ## pipeline
66
+
67
+ ```
68
+ gene sets (per cluster/niche/study)
69
+ |
70
+ v
71
+ pathway filtering (level: high/mid/low, gene type: TF, etc.)
72
+ |
73
+ v
74
+ enrichment analysis (fisher's exact test, fdr correction)
75
+ |
76
+ v
77
+ EA matrix: cluster x pathway (1-fdr or odds ratio weights)
78
+ |
79
+ v
80
+ pathway hierarchy graph (reactome) + cluster nodes with EA edges
81
+ |
82
+ v
83
+ metapath2vec random walks (weighted, type-biased)
84
+ |
85
+ v
86
+ skipgram embeddings (default 512-d)
87
+ |
88
+ v
89
+ cluster x 512 embedding matrix
90
+ ```
91
+
92
+ ## the network
93
+
94
+ the `Net` class builds a heterogeneous networkx graph with two node types:
95
+
96
+ - **pathway nodes** (reactome stIds): the reactome homo sapiens pathway hierarchy
97
+ - **cluster nodes** (your gene lists of interest): added from enrichment results
98
+
99
+ and two edge types:
100
+
101
+ - **pathway - pathway**: parent-child relations from the reactome hierarchy
102
+ - **cluster - pathway**: weighted edges from enrichment analysis (1 - fdr or odds ratio). each cluster connects to its significantly enriched pathways.
103
+
104
+ genes are not in the graph. they are used upstream in the enrichment step to determine which pathways are significant, but only pathways and clusters appear as nodes.
105
+
106
+ pathway node attributes:
107
+
108
+ - `node_type`: "sig" or "notsig" (fdr < 0.05)
109
+ - `het`: 1 (sig), 0 (notsig), or -1 (not in enrichment results)
110
+ - `feature`: fdr value (raw)
111
+ - `features`: [1 - fdr] (inverted, used as weight)
112
+ - `stId`: reactome stable identifier
113
+ - `pathway_name`: human-readable name
114
+ - `parent_pathway`: top-level reactome category
115
+
116
+ cluster node attributes:
117
+
118
+ - `node_type`: "cluster"
119
+ - `cluster`: cluster name
120
+
121
+ cluster-pathway edge attributes:
122
+
123
+ - `weight`: enrichment score (1 - fdr or odds ratio)
124
+
125
+ the graph can be filtered by pathway level (high/mid/low) and by gene membership before construction. set `digraph=True` for a directed graph, `induce=True` to keep only significant pathways.
126
+
127
+ ## the embeddings
128
+
129
+ metapath2vec performs biased random walks on the network, then trains a skipgram model to learn a dense vector for every node.
130
+
131
+ the walks are type-aware: metapaths like `[sig, notsig, sig]` or `[cluster, sig, sig]` guide the walker to follow specific node-type sequences. edge weights from the enrichment analysis bias which neighbors get visited, so clusters with strong signal to specific pathways walk there more often.
132
+
133
+ the result is a shared embedding space where:
134
+
135
+ - **pathway embeddings** (`pathway x dim`) capture where each pathway sits in the reactome hierarchy and how it relates to other pathways through enrichment patterns. pathways that are structurally close in reactome or co-enriched across clusters end up with similar vectors.
136
+ - **cluster embeddings** (`cluster x dim`) capture each cluster's biological function as a position in pathway space. two clusters with similar pathway enrichment profiles end up close together, but unlike the raw EA matrix, the embedding also encodes the hierarchical relationships between their enriched pathways. a cluster enriched in "FGFR2 alternative splicing" and one enriched in "signaling by FGFR" will be closer than two clusters enriched in unrelated pathways, even if neither shares the exact same significant pathway.
137
+ - **EA matrix** (`cluster x pathway`) is the interpretable complement to the embeddings. each row is a cluster's pathway activity profile with explicit scores (1 - fdr or odds ratio). it serves as ground truth for what the embeddings encode and can be used directly for comparison across studies via cosine similarity.
138
+
139
+ ## outputs
140
+
141
+ - **EA matrix** (`cluster x pathway`): enrichment weights per cluster, available as 1 - fdr or odds ratio
142
+ - **cluster embeddings** (`cluster x dim`): one dense vector per cluster encoding pathway activity + graph structure
143
+ - **pathway embeddings** (`pathway x dim`): one dense vector per pathway encoding hierarchical position + enrichment context
144
+
145
+ ## install
146
+
147
+ ```bash
148
+ pip install -e . # fisher enrichment path
149
+ pip install -e '.[aucell]' # adds the niche-level AUCell path (decoupler, anndata)
150
+ ```
151
+
152
+ ## usage
153
+
154
+ ### python
155
+
156
+ ```python
157
+ from gpath2vec.ea import enrich, ea_matrix, filter_pathways
158
+ from gpath2vec.net import Net
159
+ from gpath2vec.embedder import PathwayMetapath2vec
160
+
161
+ # gene sets: dict of {name: [genes]}
162
+ gene_sets = {
163
+ "cluster_0": ["EGFR", "EGF", "FGFR2", ...],
164
+ "cluster_1": ["CD8A", "CD8B", "GZMB", ...],
165
+ }
166
+
167
+ # enrichment (filter to low-level pathways containing TF genes)
168
+ ea_df = enrich(gene_sets, level="low", gene_filter=tf_genes)
169
+ matrix = ea_matrix(ea_df, weight="fdr") # cluster x pathway
170
+ matrix_or = ea_matrix(ea_df, weight="oddsratio")
171
+
172
+ # build graph with cluster nodes
173
+ clusters = {}
174
+ for _, r in ea_df[ea_df.sig_pathway].iterrows():
175
+ clusters.setdefault(r["cluster"], {})[r["stId"]] = 1 - r["fdr_bh"]
176
+
177
+ enrichment = [{"stId": r["stId"], "entities": {"fdr": r["fdr_bh"]}}
178
+ for _, r in ea_df.drop_duplicates("stId").iterrows()]
179
+
180
+ net = Net(enrichment=enrichment, id="my_study", digraph=True,
181
+ level="low", gene_filter=tf_genes, clusters=clusters)
182
+
183
+ # embeddings (pick a method)
184
+ from gpath2vec.embedder import (
185
+ PathwayMetapath2vec, SVDEmbedder, SpectralGraphEmbedder, LINEEmbedder
186
+ )
187
+
188
+ # metapath2vec: weighted random walks + skipgram on the graph
189
+ embedder = PathwayMetapath2vec(graph=net.graph, name="my_study",
190
+ walks_per_node=10, walk_length=100)
191
+ walks = embedder.model
192
+ embedder.train_embeddings(walks=walks, dimensions=512, epochs=15, lr=0.005)
193
+
194
+ # svd: truncated svd on the ea matrix (no graph, baseline)
195
+ embedder = SVDEmbedder(matrix, dimensions=512)
196
+
197
+ # spectral: laplacian eigenmaps on the graph (deterministic)
198
+ embedder = SpectralGraphEmbedder(net.graph, dimensions=512)
199
+
200
+ # line: first + second order proximity on the graph (weighted edges)
201
+ embedder = LINEEmbedder(net.graph, dimensions=512, epochs=15, lr=0.005)
202
+
203
+ embeddings = embedder.get_embeddings()
204
+ ```
205
+
206
+ ### cli
207
+
208
+ ```bash
209
+ # enrichment
210
+ gpath2vec enrichment --genes "EGFR,EGF,FGFR2" --level low --out-path results.json
211
+
212
+ # network
213
+ gpath2vec network --enrichment-path results.json --level low --out-path net.pkl
214
+
215
+ # embeddings (default: metapath2vec)
216
+ gpath2vec embeddings --network-path net.pkl --dimensions 512 --out-path emb.pkl
217
+
218
+ # embeddings with alternative methods
219
+ gpath2vec embeddings --network-path net.pkl --method svd --ea-matrix-path ea_matrix.csv --out-path emb.pkl
220
+ gpath2vec embeddings --network-path net.pkl --method spectral --out-path emb.pkl
221
+ gpath2vec embeddings --network-path net.pkl --method line --out-path emb.pkl
222
+ gpath2vec embeddings --network-path net.pkl --method vae --ea-matrix-path ea_matrix.csv --out-path emb.pkl
223
+
224
+ # full pipeline with method choice
225
+ gpath2vec end2end --genes "EGFR,EGF" --level low --method vae --output-dir output/
226
+
227
+ # niche pipeline: enrichment -> graph -> embeddings in one command.
228
+ # --enrichment fisher : binary top-N gene set per niche, fisher's exact + fdr.
229
+ # --enrichment aucell : niche-level AUCell on each niche's aggregated pseudobulk
230
+ # (one score per niche, NOT single-cell), per-niche top-k as edges.
231
+ # --aucell-standardize zscore : rank the per-niche top-k by cross-niche relative
232
+ # elevation, so pathways that are high in EVERY niche
233
+ # (the housekeeping floor) drop out. this is the cli
234
+ # default. the library `topk_per_niche()` still defaults
235
+ # to "none" (absolute score) for backward compatibility.
236
+ # inputs: --niche-matrix (niches x genes .npz/.npy), --genes (.npy gene order),
237
+ # --niche-meta (parquet with a niche_id column).
238
+ gpath2vec niche-pipeline \
239
+ --niche-matrix niches.npz --genes genes.npy --niche-meta niche_meta.parquet \
240
+ --enrichment aucell --reactome-level low --topk 50 \
241
+ --reactome-dir /path/to/reactome/cache --out-dir output/
242
+ ```
243
+
244
+ ## embedding methods
245
+
246
+ | method | input | training | edge weights | deterministic |
247
+ |--------|-------|----------|-------------|---------------|
248
+ | metapath2vec | graph | skipgram on random walks | yes (biases walks) | no |
249
+ | svd | ea matrix | truncated svd | n/a (no graph) | yes |
250
+ | spectral | graph | laplacian eigenmaps | yes | yes |
251
+ | line | graph | first + second order proximity | yes (samples proportional) | no |
252
+ | vae | ea matrix | variational autoencoder | n/a (no graph) | no |
253
+
254
+ - **metapath2vec**: best for capturing heterogeneous graph structure (pathway hierarchy + cluster nodes). requires training.
255
+ - **svd**: baseline. operates on the ea matrix directly, no graph structure. fast, deterministic. if svd gives the same results as metapath2vec, the graph isn't adding signal.
256
+ - **spectral**: deterministic embedding from the graph laplacian. good comparison point for metapath2vec without training variance.
257
+ - **line**: handles edge weights more explicitly than metapath2vec. two objectives capture both local (direct neighbors) and global (shared neighbor) structure.
258
+ - **vae**: variational autoencoder on the ea matrix. smooth latent space where similar pathway profiles map nearby. provides uncertainty estimates (latent variance per cluster) and can generate new pathway activity profiles. nonlinear alternative to svd.
259
+
260
+ all methods are more configurable from python than the cli. for example, vae exposes `beta` (kl divergence weight), `hidden_dim`, and the full model for downstream use:
261
+
262
+ ```python
263
+ from gpath2vec.embedder import VAEEmbedder
264
+
265
+ vae = VAEEmbedder(ea_matrix, dimensions=512, beta=0.5, hidden_dim=256)
266
+ embeddings = vae.get_embeddings() # latent means
267
+ uncertainty = vae.get_uncertainty() # latent variance per cluster
268
+ ```
269
+
270
+ ## pathway levels
271
+
272
+ pathway filtering uses reactome's own classification:
273
+
274
+ - **high**: pathways with enhanced high level diagrams (ehld)
275
+ - **mid**: pathways between ehld and sbgn
276
+ - **low**: pathways with sbgn diagrams (most specific)
277
+ - **all**: no filtering
278
+
279
+ ## gene filtering
280
+
281
+ restrict the pathway universe to only pathways containing specific genes of interest (ex. transcription factors from pathway commons):
282
+
283
+ ```python
284
+ # TF genes from pathway commons SIF (controls-expression-of)
285
+ ea_df = enrich(gene_sets, level="low", gene_filter=tf_genes)
286
+ ```
287
+
288
+ ## local caching
289
+
290
+ reactome data is downloaded once and cached to `~/.gpath2vec/cache/`. set `GPATH2VEC_REACTOME_DIR` to use a custom cache directory:
291
+
292
+ ```bash
293
+ export GPATH2VEC_REACTOME_DIR=/path/to/reactome/files
294
+ ```
295
+
296
+ ### offline / hpc (air-gapped compute nodes)
297
+
298
+ clusters like ARC have no internet on compute nodes, so the cache must be staged from a node that does have internet (ex. a login node):
299
+
300
+ ```bash
301
+ ./pull_reactome_cache.sh /shared/path/reactome_cache # run where there IS internet
302
+ ```
303
+
304
+ this drives the package's real fetchers, so the cache matches exactly what gpath2vec expects. on the compute node, point at it without re-downloading:
305
+
306
+ ```bash
307
+ gpath2vec niche-pipeline ... --reactome-dir /shared/path/reactome_cache
308
+ # or: export GPATH2VEC_REACTOME_DIR=/shared/path/reactome_cache
309
+ ```
310
+
311
+ ## reproducibility
312
+
313
+ all stochastic embedders (metapath2vec, line, vae) take a `seed` (default 1234) that pins the python, numpy and torch rngs, so embeddings are bit-reproducible run to run. the seed is re-applied before training (independent of walk-generation rng) and recorded in `run_provenance.json`. svd and spectral are deterministic by construction. cli: `--seed`.
314
+
315
+ ## citation
316
+
317
+ archived on zenodo. the DOI below is the *concept* DOI: it always resolves to
318
+ the newest release, so it stays correct as versions are added.
319
+
320
+ > Sanati, N. (2026). *gpath2vec: Pathway-Informed Feature Embeddings for
321
+ > Biological Observations from Gene Sets*. Zenodo.
322
+ > https://doi.org/10.5281/zenodo.22681141
323
+
324
+ ```bibtex
325
+ @software{sanati_gpath2vec,
326
+ author = {Sanati, Nasim},
327
+ title = {{gpath2vec: Pathway-Informed Feature Embeddings for
328
+ Biological Observations from Gene Sets}},
329
+ year = {2026},
330
+ publisher = {Zenodo},
331
+ doi = {10.5281/zenodo.22681141},
332
+ url = {https://doi.org/10.5281/zenodo.22681141}
333
+ }
334
+ ```
335
+
336
+ to cite the exact snapshot rather than the latest release, use the version DOI
337
+ for v3.0.0: `10.5281/zenodo.22681142`.
338
+
339
+ ## todo
340
+
341
+ - **edge2vec**: edge-type transition-matrix biased walks as an embedding method.
342
+ - **lorentz (hyperbolic) pipeline**
@@ -0,0 +1,292 @@
1
+ # gpath2vec
2
+
3
+ ![Status](https://img.shields.io/badge/Status-Build%20Passing-lgreen)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22681141.svg)](https://doi.org/10.5281/zenodo.22681141)
6
+
7
+ a python package for converting gene sets to biological pathway embeddings with enrichment analysis attributes.
8
+
9
+ gene sets (from clusters, niches, studies) are tested against reactome pathways via fisher's exact test, then embedded into a shared vector space using metapath2vec over the pathway hierarchy graph.
10
+
11
+ enrichment is either fisher's exact (a binary top-N gene set per cluster/niche) or **niche-level AUCell** (the full per-niche expression ranking, no gene-set selection step). AUCell here scores each niche's **aggregated pseudobulk** profile, one score per (niche, pathway); it is **not single-cell AUCell** (the package never sees individual cells, niche construction is upstream). both enrichment sources feed the same reactome hierarchy graph and metapath2vec embedding.
12
+
13
+ ![gpath2vec](https://raw.githubusercontent.com/teslajoy/gpath2vec/main/img/gpath2vec.png)
14
+
15
+ ## pipeline
16
+
17
+ ```
18
+ gene sets (per cluster/niche/study)
19
+ |
20
+ v
21
+ pathway filtering (level: high/mid/low, gene type: TF, etc.)
22
+ |
23
+ v
24
+ enrichment analysis (fisher's exact test, fdr correction)
25
+ |
26
+ v
27
+ EA matrix: cluster x pathway (1-fdr or odds ratio weights)
28
+ |
29
+ v
30
+ pathway hierarchy graph (reactome) + cluster nodes with EA edges
31
+ |
32
+ v
33
+ metapath2vec random walks (weighted, type-biased)
34
+ |
35
+ v
36
+ skipgram embeddings (default 512-d)
37
+ |
38
+ v
39
+ cluster x 512 embedding matrix
40
+ ```
41
+
42
+ ## the network
43
+
44
+ the `Net` class builds a heterogeneous networkx graph with two node types:
45
+
46
+ - **pathway nodes** (reactome stIds): the reactome homo sapiens pathway hierarchy
47
+ - **cluster nodes** (your gene lists of interest): added from enrichment results
48
+
49
+ and two edge types:
50
+
51
+ - **pathway - pathway**: parent-child relations from the reactome hierarchy
52
+ - **cluster - pathway**: weighted edges from enrichment analysis (1 - fdr or odds ratio). each cluster connects to its significantly enriched pathways.
53
+
54
+ genes are not in the graph. they are used upstream in the enrichment step to determine which pathways are significant, but only pathways and clusters appear as nodes.
55
+
56
+ pathway node attributes:
57
+
58
+ - `node_type`: "sig" or "notsig" (fdr < 0.05)
59
+ - `het`: 1 (sig), 0 (notsig), or -1 (not in enrichment results)
60
+ - `feature`: fdr value (raw)
61
+ - `features`: [1 - fdr] (inverted, used as weight)
62
+ - `stId`: reactome stable identifier
63
+ - `pathway_name`: human-readable name
64
+ - `parent_pathway`: top-level reactome category
65
+
66
+ cluster node attributes:
67
+
68
+ - `node_type`: "cluster"
69
+ - `cluster`: cluster name
70
+
71
+ cluster-pathway edge attributes:
72
+
73
+ - `weight`: enrichment score (1 - fdr or odds ratio)
74
+
75
+ the graph can be filtered by pathway level (high/mid/low) and by gene membership before construction. set `digraph=True` for a directed graph, `induce=True` to keep only significant pathways.
76
+
77
+ ## the embeddings
78
+
79
+ metapath2vec performs biased random walks on the network, then trains a skipgram model to learn a dense vector for every node.
80
+
81
+ the walks are type-aware: metapaths like `[sig, notsig, sig]` or `[cluster, sig, sig]` guide the walker to follow specific node-type sequences. edge weights from the enrichment analysis bias which neighbors get visited, so clusters with strong signal to specific pathways walk there more often.
82
+
83
+ the result is a shared embedding space where:
84
+
85
+ - **pathway embeddings** (`pathway x dim`) capture where each pathway sits in the reactome hierarchy and how it relates to other pathways through enrichment patterns. pathways that are structurally close in reactome or co-enriched across clusters end up with similar vectors.
86
+ - **cluster embeddings** (`cluster x dim`) capture each cluster's biological function as a position in pathway space. two clusters with similar pathway enrichment profiles end up close together, but unlike the raw EA matrix, the embedding also encodes the hierarchical relationships between their enriched pathways. a cluster enriched in "FGFR2 alternative splicing" and one enriched in "signaling by FGFR" will be closer than two clusters enriched in unrelated pathways, even if neither shares the exact same significant pathway.
87
+ - **EA matrix** (`cluster x pathway`) is the interpretable complement to the embeddings. each row is a cluster's pathway activity profile with explicit scores (1 - fdr or odds ratio). it serves as ground truth for what the embeddings encode and can be used directly for comparison across studies via cosine similarity.
88
+
89
+ ## outputs
90
+
91
+ - **EA matrix** (`cluster x pathway`): enrichment weights per cluster, available as 1 - fdr or odds ratio
92
+ - **cluster embeddings** (`cluster x dim`): one dense vector per cluster encoding pathway activity + graph structure
93
+ - **pathway embeddings** (`pathway x dim`): one dense vector per pathway encoding hierarchical position + enrichment context
94
+
95
+ ## install
96
+
97
+ ```bash
98
+ pip install -e . # fisher enrichment path
99
+ pip install -e '.[aucell]' # adds the niche-level AUCell path (decoupler, anndata)
100
+ ```
101
+
102
+ ## usage
103
+
104
+ ### python
105
+
106
+ ```python
107
+ from gpath2vec.ea import enrich, ea_matrix, filter_pathways
108
+ from gpath2vec.net import Net
109
+ from gpath2vec.embedder import PathwayMetapath2vec
110
+
111
+ # gene sets: dict of {name: [genes]}
112
+ gene_sets = {
113
+ "cluster_0": ["EGFR", "EGF", "FGFR2", ...],
114
+ "cluster_1": ["CD8A", "CD8B", "GZMB", ...],
115
+ }
116
+
117
+ # enrichment (filter to low-level pathways containing TF genes)
118
+ ea_df = enrich(gene_sets, level="low", gene_filter=tf_genes)
119
+ matrix = ea_matrix(ea_df, weight="fdr") # cluster x pathway
120
+ matrix_or = ea_matrix(ea_df, weight="oddsratio")
121
+
122
+ # build graph with cluster nodes
123
+ clusters = {}
124
+ for _, r in ea_df[ea_df.sig_pathway].iterrows():
125
+ clusters.setdefault(r["cluster"], {})[r["stId"]] = 1 - r["fdr_bh"]
126
+
127
+ enrichment = [{"stId": r["stId"], "entities": {"fdr": r["fdr_bh"]}}
128
+ for _, r in ea_df.drop_duplicates("stId").iterrows()]
129
+
130
+ net = Net(enrichment=enrichment, id="my_study", digraph=True,
131
+ level="low", gene_filter=tf_genes, clusters=clusters)
132
+
133
+ # embeddings (pick a method)
134
+ from gpath2vec.embedder import (
135
+ PathwayMetapath2vec, SVDEmbedder, SpectralGraphEmbedder, LINEEmbedder
136
+ )
137
+
138
+ # metapath2vec: weighted random walks + skipgram on the graph
139
+ embedder = PathwayMetapath2vec(graph=net.graph, name="my_study",
140
+ walks_per_node=10, walk_length=100)
141
+ walks = embedder.model
142
+ embedder.train_embeddings(walks=walks, dimensions=512, epochs=15, lr=0.005)
143
+
144
+ # svd: truncated svd on the ea matrix (no graph, baseline)
145
+ embedder = SVDEmbedder(matrix, dimensions=512)
146
+
147
+ # spectral: laplacian eigenmaps on the graph (deterministic)
148
+ embedder = SpectralGraphEmbedder(net.graph, dimensions=512)
149
+
150
+ # line: first + second order proximity on the graph (weighted edges)
151
+ embedder = LINEEmbedder(net.graph, dimensions=512, epochs=15, lr=0.005)
152
+
153
+ embeddings = embedder.get_embeddings()
154
+ ```
155
+
156
+ ### cli
157
+
158
+ ```bash
159
+ # enrichment
160
+ gpath2vec enrichment --genes "EGFR,EGF,FGFR2" --level low --out-path results.json
161
+
162
+ # network
163
+ gpath2vec network --enrichment-path results.json --level low --out-path net.pkl
164
+
165
+ # embeddings (default: metapath2vec)
166
+ gpath2vec embeddings --network-path net.pkl --dimensions 512 --out-path emb.pkl
167
+
168
+ # embeddings with alternative methods
169
+ gpath2vec embeddings --network-path net.pkl --method svd --ea-matrix-path ea_matrix.csv --out-path emb.pkl
170
+ gpath2vec embeddings --network-path net.pkl --method spectral --out-path emb.pkl
171
+ gpath2vec embeddings --network-path net.pkl --method line --out-path emb.pkl
172
+ gpath2vec embeddings --network-path net.pkl --method vae --ea-matrix-path ea_matrix.csv --out-path emb.pkl
173
+
174
+ # full pipeline with method choice
175
+ gpath2vec end2end --genes "EGFR,EGF" --level low --method vae --output-dir output/
176
+
177
+ # niche pipeline: enrichment -> graph -> embeddings in one command.
178
+ # --enrichment fisher : binary top-N gene set per niche, fisher's exact + fdr.
179
+ # --enrichment aucell : niche-level AUCell on each niche's aggregated pseudobulk
180
+ # (one score per niche, NOT single-cell), per-niche top-k as edges.
181
+ # --aucell-standardize zscore : rank the per-niche top-k by cross-niche relative
182
+ # elevation, so pathways that are high in EVERY niche
183
+ # (the housekeeping floor) drop out. this is the cli
184
+ # default. the library `topk_per_niche()` still defaults
185
+ # to "none" (absolute score) for backward compatibility.
186
+ # inputs: --niche-matrix (niches x genes .npz/.npy), --genes (.npy gene order),
187
+ # --niche-meta (parquet with a niche_id column).
188
+ gpath2vec niche-pipeline \
189
+ --niche-matrix niches.npz --genes genes.npy --niche-meta niche_meta.parquet \
190
+ --enrichment aucell --reactome-level low --topk 50 \
191
+ --reactome-dir /path/to/reactome/cache --out-dir output/
192
+ ```
193
+
194
+ ## embedding methods
195
+
196
+ | method | input | training | edge weights | deterministic |
197
+ |--------|-------|----------|-------------|---------------|
198
+ | metapath2vec | graph | skipgram on random walks | yes (biases walks) | no |
199
+ | svd | ea matrix | truncated svd | n/a (no graph) | yes |
200
+ | spectral | graph | laplacian eigenmaps | yes | yes |
201
+ | line | graph | first + second order proximity | yes (samples proportional) | no |
202
+ | vae | ea matrix | variational autoencoder | n/a (no graph) | no |
203
+
204
+ - **metapath2vec**: best for capturing heterogeneous graph structure (pathway hierarchy + cluster nodes). requires training.
205
+ - **svd**: baseline. operates on the ea matrix directly, no graph structure. fast, deterministic. if svd gives the same results as metapath2vec, the graph isn't adding signal.
206
+ - **spectral**: deterministic embedding from the graph laplacian. good comparison point for metapath2vec without training variance.
207
+ - **line**: handles edge weights more explicitly than metapath2vec. two objectives capture both local (direct neighbors) and global (shared neighbor) structure.
208
+ - **vae**: variational autoencoder on the ea matrix. smooth latent space where similar pathway profiles map nearby. provides uncertainty estimates (latent variance per cluster) and can generate new pathway activity profiles. nonlinear alternative to svd.
209
+
210
+ all methods are more configurable from python than the cli. for example, vae exposes `beta` (kl divergence weight), `hidden_dim`, and the full model for downstream use:
211
+
212
+ ```python
213
+ from gpath2vec.embedder import VAEEmbedder
214
+
215
+ vae = VAEEmbedder(ea_matrix, dimensions=512, beta=0.5, hidden_dim=256)
216
+ embeddings = vae.get_embeddings() # latent means
217
+ uncertainty = vae.get_uncertainty() # latent variance per cluster
218
+ ```
219
+
220
+ ## pathway levels
221
+
222
+ pathway filtering uses reactome's own classification:
223
+
224
+ - **high**: pathways with enhanced high level diagrams (ehld)
225
+ - **mid**: pathways between ehld and sbgn
226
+ - **low**: pathways with sbgn diagrams (most specific)
227
+ - **all**: no filtering
228
+
229
+ ## gene filtering
230
+
231
+ restrict the pathway universe to only pathways containing specific genes of interest (ex. transcription factors from pathway commons):
232
+
233
+ ```python
234
+ # TF genes from pathway commons SIF (controls-expression-of)
235
+ ea_df = enrich(gene_sets, level="low", gene_filter=tf_genes)
236
+ ```
237
+
238
+ ## local caching
239
+
240
+ reactome data is downloaded once and cached to `~/.gpath2vec/cache/`. set `GPATH2VEC_REACTOME_DIR` to use a custom cache directory:
241
+
242
+ ```bash
243
+ export GPATH2VEC_REACTOME_DIR=/path/to/reactome/files
244
+ ```
245
+
246
+ ### offline / hpc (air-gapped compute nodes)
247
+
248
+ clusters like ARC have no internet on compute nodes, so the cache must be staged from a node that does have internet (ex. a login node):
249
+
250
+ ```bash
251
+ ./pull_reactome_cache.sh /shared/path/reactome_cache # run where there IS internet
252
+ ```
253
+
254
+ this drives the package's real fetchers, so the cache matches exactly what gpath2vec expects. on the compute node, point at it without re-downloading:
255
+
256
+ ```bash
257
+ gpath2vec niche-pipeline ... --reactome-dir /shared/path/reactome_cache
258
+ # or: export GPATH2VEC_REACTOME_DIR=/shared/path/reactome_cache
259
+ ```
260
+
261
+ ## reproducibility
262
+
263
+ all stochastic embedders (metapath2vec, line, vae) take a `seed` (default 1234) that pins the python, numpy and torch rngs, so embeddings are bit-reproducible run to run. the seed is re-applied before training (independent of walk-generation rng) and recorded in `run_provenance.json`. svd and spectral are deterministic by construction. cli: `--seed`.
264
+
265
+ ## citation
266
+
267
+ archived on zenodo. the DOI below is the *concept* DOI: it always resolves to
268
+ the newest release, so it stays correct as versions are added.
269
+
270
+ > Sanati, N. (2026). *gpath2vec: Pathway-Informed Feature Embeddings for
271
+ > Biological Observations from Gene Sets*. Zenodo.
272
+ > https://doi.org/10.5281/zenodo.22681141
273
+
274
+ ```bibtex
275
+ @software{sanati_gpath2vec,
276
+ author = {Sanati, Nasim},
277
+ title = {{gpath2vec: Pathway-Informed Feature Embeddings for
278
+ Biological Observations from Gene Sets}},
279
+ year = {2026},
280
+ publisher = {Zenodo},
281
+ doi = {10.5281/zenodo.22681141},
282
+ url = {https://doi.org/10.5281/zenodo.22681141}
283
+ }
284
+ ```
285
+
286
+ to cite the exact snapshot rather than the latest release, use the version DOI
287
+ for v3.0.0: `10.5281/zenodo.22681142`.
288
+
289
+ ## todo
290
+
291
+ - **edge2vec**: edge-type transition-matrix biased walks as an embedding method.
292
+ - **lorentz (hyperbolic) pipeline**
@@ -0,0 +1,10 @@
1
+ from .embedder import (
2
+ Embedder, PathwayMetapath2vec, SVDEmbedder,
3
+ SpectralGraphEmbedder, LINEEmbedder, VAEEmbedder
4
+ )
5
+ from .net import Net
6
+ from . import ea
7
+ from . import utils
8
+ from . import compare
9
+
10
+ __version__ = "1.0.0"