oncoordinate 0.1.7__py3-none-any.whl

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 (62) hide show
  1. oncoordinate/HallmarkPathGMT/HALLMARK_ADIPOGENESIS.v2023.2.Hs.gmt +1 -0
  2. oncoordinate/HallmarkPathGMT/HALLMARK_ALLOGRAFT_REJECTION.v2023.2.Hs.gmt +1 -0
  3. oncoordinate/HallmarkPathGMT/HALLMARK_ANDROGEN_RESPONSE.v2023.2.Hs.gmt +1 -0
  4. oncoordinate/HallmarkPathGMT/HALLMARK_ANGIOGENESIS.v2023.2.Hs.gmt +1 -0
  5. oncoordinate/HallmarkPathGMT/HALLMARK_APICAL_JUNCTION.v2023.2.Hs.gmt +1 -0
  6. oncoordinate/HallmarkPathGMT/HALLMARK_APICAL_SURFACE.v2023.2.Hs.gmt +1 -0
  7. oncoordinate/HallmarkPathGMT/HALLMARK_APOPTOSIS.v2023.2.Hs.gmt +1 -0
  8. oncoordinate/HallmarkPathGMT/HALLMARK_BILE_ACID_METABOLISM.v2023.2.Hs.gmt +1 -0
  9. oncoordinate/HallmarkPathGMT/HALLMARK_CHOLESTEROL_HOMEOSTASIS.v2023.2.Hs.gmt +1 -0
  10. oncoordinate/HallmarkPathGMT/HALLMARK_COAGULATION.v2023.2.Hs.gmt +1 -0
  11. oncoordinate/HallmarkPathGMT/HALLMARK_COMPLEMENT.v2023.2.Hs.gmt +1 -0
  12. oncoordinate/HallmarkPathGMT/HALLMARK_DNA_REPAIR.v2023.2.Hs.gmt +1 -0
  13. oncoordinate/HallmarkPathGMT/HALLMARK_E2F_TARGETS.v2023.2.Hs.gmt +1 -0
  14. oncoordinate/HallmarkPathGMT/HALLMARK_EPITHELIAL_MESENCHYMAL_TRANSITION.v2023.2.Hs.gmt +1 -0
  15. oncoordinate/HallmarkPathGMT/HALLMARK_ESTROGEN_RESPONSE_EARLY.v2023.2.Hs.gmt +1 -0
  16. oncoordinate/HallmarkPathGMT/HALLMARK_ESTROGEN_RESPONSE_LATE.v2023.2.Hs.gmt +1 -0
  17. oncoordinate/HallmarkPathGMT/HALLMARK_FATTY_ACID_METABOLISM.v2023.2.Hs.gmt +1 -0
  18. oncoordinate/HallmarkPathGMT/HALLMARK_G2M_CHECKPOINT.v2023.2.Hs.gmt +1 -0
  19. oncoordinate/HallmarkPathGMT/HALLMARK_GLYCOLYSIS.v2023.2.Hs.gmt +1 -0
  20. oncoordinate/HallmarkPathGMT/HALLMARK_HEDGEHOG_SIGNALING.v2023.2.Hs.gmt +1 -0
  21. oncoordinate/HallmarkPathGMT/HALLMARK_HEME_METABOLISM.v2023.2.Hs.gmt +1 -0
  22. oncoordinate/HallmarkPathGMT/HALLMARK_HYPOXIA.v2023.2.Hs.gmt +1 -0
  23. oncoordinate/HallmarkPathGMT/HALLMARK_IL2_STAT5_SIGNALING.v2023.2.Hs.gmt +1 -0
  24. oncoordinate/HallmarkPathGMT/HALLMARK_IL6_JAK_STAT3_SIGNALING.v2023.2.Hs.gmt +1 -0
  25. oncoordinate/HallmarkPathGMT/HALLMARK_INFLAMMATORY_RESPONSE.v2023.2.Hs.gmt +1 -0
  26. oncoordinate/HallmarkPathGMT/HALLMARK_INTERFERON_ALPHA_RESPONSE.v2023.2.Hs.gmt +1 -0
  27. oncoordinate/HallmarkPathGMT/HALLMARK_INTERFERON_GAMMA_RESPONSE.v2023.2.Hs.gmt +1 -0
  28. oncoordinate/HallmarkPathGMT/HALLMARK_KRAS_SIGNALING_DN.v2023.2.Hs.gmt +1 -0
  29. oncoordinate/HallmarkPathGMT/HALLMARK_KRAS_SIGNALING_UP.v2023.2.Hs.gmt +1 -0
  30. oncoordinate/HallmarkPathGMT/HALLMARK_MITOTIC_SPINDLE.v2023.2.Hs.gmt +1 -0
  31. oncoordinate/HallmarkPathGMT/HALLMARK_MTORC1_SIGNALING.v2023.2.Hs.gmt +1 -0
  32. oncoordinate/HallmarkPathGMT/HALLMARK_MYC_TARGETS_V1.v2023.2.Hs.gmt +1 -0
  33. oncoordinate/HallmarkPathGMT/HALLMARK_MYC_TARGETS_V2.v2023.2.Hs.gmt +1 -0
  34. oncoordinate/HallmarkPathGMT/HALLMARK_MYOGENESIS.v2023.2.Hs.gmt +1 -0
  35. oncoordinate/HallmarkPathGMT/HALLMARK_NOTCH_SIGNALING.v2023.2.Hs.gmt +1 -0
  36. oncoordinate/HallmarkPathGMT/HALLMARK_OXIDATIVE_PHOSPHORYLATION.v2023.2.Hs.gmt +1 -0
  37. oncoordinate/HallmarkPathGMT/HALLMARK_P53_PATHWAY.v2023.2.Hs.gmt +1 -0
  38. oncoordinate/HallmarkPathGMT/HALLMARK_PANCREAS_BETA_CELLS.v2023.2.Hs.gmt +1 -0
  39. oncoordinate/HallmarkPathGMT/HALLMARK_PEROXISOME.v2023.2.Hs.gmt +1 -0
  40. oncoordinate/HallmarkPathGMT/HALLMARK_PI3K_AKT_MTOR_SIGNALING.v2023.2.Hs.gmt +1 -0
  41. oncoordinate/HallmarkPathGMT/HALLMARK_PROTEIN_SECRETION.v2023.2.Hs.gmt +1 -0
  42. oncoordinate/HallmarkPathGMT/HALLMARK_REACTIVE_OXYGEN_SPECIES_PATHWAY.v2023.2.Hs.gmt +1 -0
  43. oncoordinate/HallmarkPathGMT/HALLMARK_SPERMATOGENESIS.v2023.2.Hs.gmt +1 -0
  44. oncoordinate/HallmarkPathGMT/HALLMARK_TGF_BETA_SIGNALING.v2023.2.Hs.gmt +1 -0
  45. oncoordinate/HallmarkPathGMT/HALLMARK_TNFA_SIGNALING_VIA_NFKB.v2023.2.Hs.gmt +1 -0
  46. oncoordinate/HallmarkPathGMT/HALLMARK_UNFOLDED_PROTEIN_RESPONSE.v2023.2.Hs.gmt +1 -0
  47. oncoordinate/HallmarkPathGMT/HALLMARK_UV_RESPONSE_DN.v2023.2.Hs.gmt +1 -0
  48. oncoordinate/HallmarkPathGMT/HALLMARK_UV_RESPONSE_UP.v2023.2.Hs.gmt +1 -0
  49. oncoordinate/HallmarkPathGMT/HALLMARK_WNT_BETA_CATENIN_SIGNALING.v2023.2.Hs.gmt +1 -0
  50. oncoordinate/HallmarkPathGMT/HALLMARK_XENOBIOTIC_METABOLISM.v2023.2.Hs.gmt +1 -0
  51. oncoordinate/HallmarkPathGMT/REACTOME_CELL_CYCLE.v2023.2.Hs.gmt +1 -0
  52. oncoordinate/HallmarkPathGMT/REACTOME_SIGNALING_BY_EGFR_IN_CANCER.v2023.2.Hs.gmt +1 -0
  53. oncoordinate/__init__.py +0 -0
  54. oncoordinate/lt.py +472 -0
  55. oncoordinate/oncoordinate.joblib +0 -0
  56. oncoordinate/sc.py +729 -0
  57. oncoordinate/sp.py +513 -0
  58. oncoordinate-0.1.7.dist-info/METADATA +93 -0
  59. oncoordinate-0.1.7.dist-info/RECORD +62 -0
  60. oncoordinate-0.1.7.dist-info/WHEEL +5 -0
  61. oncoordinate-0.1.7.dist-info/licenses/LICENSE +21 -0
  62. oncoordinate-0.1.7.dist-info/top_level.txt +1 -0
oncoordinate/sp.py ADDED
@@ -0,0 +1,513 @@
1
+ # sp.py (under oncoordinate)
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Optional, Union, Sequence, Dict, Any, List
7
+
8
+ import anndata as ad
9
+ import numpy as np
10
+ import pandas as pd
11
+ from scipy import sparse
12
+ from sklearn.neighbors import NearestNeighbors
13
+ from sklearn.cluster import KMeans
14
+ import scanpy.external as sce
15
+ from pathlib import Path
16
+ import scanpy as sc
17
+ import imageio as iio
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ def _load_tissue_positions(sp_dir: Path, obs_index: pd.Index) -> pd.DataFrame:
22
+ pos_files = list(sp_dir.glob("*tissue_positions*.csv"))
23
+ if not pos_files:
24
+ raise FileNotFoundError(f"No tissue_positions file found in {sp_dir}")
25
+
26
+ pos_path = pos_files[0]
27
+
28
+ pos = pd.read_csv(pos_path, header=None)
29
+ if pos.shape[1] == 6:
30
+ pos.columns = ["barcode", "in_tissue",
31
+ "array_row", "array_col",
32
+ "pxl_row", "pxl_col"]
33
+ else:
34
+ pos = pd.read_csv(pos_path)
35
+
36
+ if "barcode" not in pos.columns:
37
+ raise ValueError(f"No 'barcode' column in {pos_path}")
38
+
39
+ pos["barcode"] = pos["barcode"].astype(str)
40
+ obs_index = obs_index.astype(str)
41
+ direct_match = pos["barcode"].isin(obs_index).sum()
42
+
43
+ if direct_match == 0:
44
+ pos["_barcode_alt"] = pos["barcode"] + "-1"
45
+ alt_match = pos["_barcode_alt"].isin(obs_index).sum()
46
+ if alt_match > direct_match:
47
+ pos["barcode"] = pos["_barcode_alt"]
48
+ pos = pos.drop(columns=["_barcode_alt"], errors="ignore")
49
+
50
+ pos = pos.set_index("barcode")
51
+ return pos
52
+
53
+ def _load_visium_images_and_scalefactors(sp_dir: Path) -> Dict[str, Any]:
54
+ images: Dict[str, np.ndarray] = {}
55
+ scalefactors: Dict[str, float] = {}
56
+
57
+ sf_path = sp_dir / "scalefactors_json.json"
58
+ if sf_path.exists():
59
+ with open(sf_path, "r") as f:
60
+ scalefactors = json.load(f)
61
+
62
+ hires_png = list(sp_dir.glob("*tissue_hires_image.png"))
63
+ lowres_png = list(sp_dir.glob("*tissue_lowres_image.png"))
64
+ tif_imgs = list(sp_dir.glob("*.tif"))
65
+
66
+ if hires_png:
67
+ images["hires"] = iio.imread(hires_png[0])
68
+
69
+ if lowres_png:
70
+ images["lowres"] = iio.imread(lowres_png[0])
71
+
72
+ if tif_imgs:
73
+ images["fullres"] = iio.imread(tif_imgs[0])
74
+
75
+ scalefactors.setdefault("spot_diameter_fullres", 65.0)
76
+
77
+ if "tissue_hires_scalef" not in scalefactors:
78
+ scalefactors["tissue_hires_scalef"] = 1.0
79
+
80
+ if "tissue_lowres_scalef" not in scalefactors:
81
+ scalefactors["tissue_lowres_scalef"] = 1.0
82
+
83
+ return {
84
+ "images": images,
85
+ "scalefactors": scalefactors,
86
+ "metadata": {},
87
+ }
88
+
89
+ def _attach_visium_spatial(a: ad.AnnData, p: Path, sample_name: str) -> ad.AnnData:
90
+ sp_dir = p / "spatial"
91
+ if not sp_dir.is_dir():
92
+ return a
93
+
94
+ try:
95
+ pos = _load_tissue_positions(sp_dir, a.obs.index)
96
+ except FileNotFoundError:
97
+ logger.warning("No tissue positions found in %s", sp_dir)
98
+ return a
99
+
100
+ a.obs = a.obs.join(pos, how="left")
101
+
102
+ if "in_tissue" in a.obs.columns:
103
+ a.obs["in_tissue"] = a.obs["in_tissue"].fillna(0).astype(int)
104
+ n_before = a.n_obs
105
+ mask_tissue = a.obs["in_tissue"] == 1
106
+ if mask_tissue.sum() > 0:
107
+ a = a[mask_tissue].copy()
108
+ logger.info(
109
+ "Subset sample '%s' to in_tissue==1: %d -> %d spots",
110
+ sample_name,
111
+ n_before,
112
+ a.n_obs,
113
+ )
114
+ else:
115
+ logger.warning(
116
+ "Sample '%s' has no barcodes with in_tissue==1; keeping all %d spots.",
117
+ sample_name,
118
+ n_before,
119
+ )
120
+
121
+ if {"pxl_row", "pxl_col"}.issubset(a.obs.columns):
122
+ coords = a.obs[["pxl_col", "pxl_row"]].to_numpy()
123
+ elif {"array_row", "array_col"}.issubset(a.obs.columns):
124
+ coords = a.obs[["array_col", "array_row"]].to_numpy()
125
+ else:
126
+ logger.warning(
127
+ "No pixel/array coordinates found in tissue_positions for %s; "
128
+ "skipping spatial embedding.",
129
+ p,
130
+ )
131
+ return a
132
+
133
+ a.obsm["spatial"] = coords
134
+
135
+ spatial_meta = _load_visium_images_and_scalefactors(sp_dir)
136
+ a.uns.setdefault("spatial", {})
137
+ a.uns["spatial"][sample_name] = spatial_meta
138
+
139
+ return a
140
+
141
+
142
+ def _read_single_spatial(path_like: Union[str, Path]) -> ad.AnnData:
143
+ p = Path(path_like)
144
+ if p.is_dir():
145
+ sp_dir = p / "spatial"
146
+ has_spatial = sp_dir.is_dir() and any(sp_dir.glob("*tissue_positions*.csv"))
147
+
148
+ if has_spatial:
149
+ if (p / "filtered_feature_bc_matrix.h5").exists():
150
+ a = sc.read_10x_h5(p / "filtered_feature_bc_matrix.h5")
151
+ elif (p / "matrix.mtx.gz").exists() or (p / "matrix.mtx").exists():
152
+ a = sc.read_10x_mtx(p, var_names="gene_symbols")
153
+ else:
154
+ a = sc.read_visium(path=p)
155
+
156
+ sample_name = p.name
157
+ a = _attach_visium_spatial(a, p, sample_name)
158
+
159
+ elif (p / "filtered_feature_bc_matrix.h5").exists() or (p / "raw_feature_bc_matrix.h5").exists():
160
+ a = sc.read_visium(path=p)
161
+ sample_name = p.name
162
+
163
+ elif (p / "matrix.mtx.gz").exists() or (p / "matrix.mtx").exists():
164
+ a = sc.read_10x_mtx(p, var_names="gene_symbols")
165
+ sample_name = p.name
166
+
167
+ else:
168
+ raise ValueError(
169
+ f"Directory {p} does not look like a Visium or 10x mtx folder."
170
+ )
171
+
172
+ else:
173
+ suffix = p.suffix.lower()
174
+ if suffix == ".h5ad":
175
+ a = ad.read_h5ad(p)
176
+ sample_name = p.stem
177
+ elif suffix == ".h5":
178
+ try:
179
+ a = sc.read_10x_h5(p)
180
+ except Exception:
181
+ a = ad.read_h5ad(p)
182
+ sample_name = p.stem
183
+ else:
184
+ raise ValueError(f"Unsupported spatial input: {p}")
185
+
186
+ a.obs.index = a.obs.index.astype(str)
187
+ a.var.index = a.var.index.astype(str)
188
+ a.var_names = a.var_names.astype(str).str.upper()
189
+ a.var_names_make_unique()
190
+
191
+ if "sample" not in a.obs.columns:
192
+ a.obs["sample"] = sample_name
193
+
194
+ if "counts" not in a.layers:
195
+ a.layers["counts"] = a.X.copy()
196
+
197
+ return a
198
+
199
+ def load_spatial(
200
+ path: Union[str, Path],
201
+ *,
202
+ sample_key: str = "sample",
203
+ ) -> ad.AnnData:
204
+ p = Path(path)
205
+ a = _read_single_spatial(p)
206
+
207
+ if sample_key != "sample":
208
+ if "sample" in a.obs.columns:
209
+ a.obs[sample_key] = a.obs["sample"].astype(str)
210
+ else:
211
+ label = p.stem
212
+ a.obs[sample_key] = label
213
+
214
+ return a
215
+
216
+ def preprocess_spatial(
217
+ adata: ad.AnnData,
218
+ *,
219
+ sample_key: str = "sample",
220
+ batch_key: Optional[str] = None,
221
+ n_hvg: int = 2000,
222
+ pca_n_comps: int = 50,
223
+ neighbors_n_pcs: int = 30,
224
+ neighbors_k: int = 15,
225
+ use_batch_correction: bool = True,
226
+ inplace: bool = True,
227
+ ) -> ad.AnnData:
228
+ if not inplace:
229
+ adata = adata.copy()
230
+
231
+ adata.obs.index = adata.obs.index.astype(str)
232
+ adata.var.index = adata.var.index.astype(str)
233
+ adata.var_names = adata.var_names.astype(str)
234
+
235
+ if "counts" not in adata.layers:
236
+ adata.layers["counts"] = adata.X.copy()
237
+
238
+ layer = "counts"
239
+ hvg_batch_key = batch_key if batch_key is not None and batch_key in adata.obs.columns else None
240
+
241
+ sc.pp.highly_variable_genes(
242
+ adata,
243
+ layer=layer,
244
+ n_top_genes=n_hvg,
245
+ flavor="seurat_v3",
246
+ batch_key=hvg_batch_key,
247
+ inplace=True,
248
+ )
249
+ adata = adata[:, adata.var["highly_variable"].values].copy()
250
+
251
+ sc.pp.normalize_total(adata, target_sum=1e4)
252
+ sc.pp.log1p(adata)
253
+ max_rank = max(0, min(adata.n_obs, adata.n_vars) - 1)
254
+ n_comps = max(2, min(pca_n_comps, max_rank))
255
+ sc.tl.pca(adata, n_comps=n_comps, svd_solver="arpack")
256
+
257
+ n_pcs_avail = int(adata.obsm["X_pca"].shape[1])
258
+ n_pcs_use = min(neighbors_n_pcs, n_pcs_avail)
259
+ k = max(2, min(neighbors_k, max(2, adata.n_obs - 1)))
260
+
261
+ if batch_key is None:
262
+ batch_key = sample_key
263
+
264
+ use_bbknn = (
265
+ use_batch_correction
266
+ and sce is not None
267
+ and batch_key is not None
268
+ and batch_key in adata.obs.columns
269
+ )
270
+
271
+ if use_bbknn:
272
+ try:
273
+ sce.pp.bbknn(adata, batch_key=batch_key, n_pcs=n_pcs_use)
274
+ except Exception as e:
275
+ logger.warning(
276
+ "BBKNN batch correction failed (%s); falling back to standard neighbors.",
277
+ e,
278
+ )
279
+ sc.pp.neighbors(adata, n_neighbors=k, n_pcs=n_pcs_use)
280
+ else:
281
+ sc.pp.neighbors(adata, n_neighbors=k, n_pcs=n_pcs_use)
282
+
283
+ sc.tl.umap(adata)
284
+
285
+ adata.uns.setdefault("oncoordinate_spatial_params", {})
286
+ adata.uns["oncoordinate_spatial_params"].update(
287
+ dict(
288
+ sample_key=sample_key,
289
+ batch_key=batch_key,
290
+ n_hvg=int(n_hvg),
291
+ pca_n_comps=int(n_comps),
292
+ neighbors_n_pcs=int(n_pcs_use),
293
+ neighbors_k=int(k),
294
+ use_batch_correction=bool(use_batch_correction),
295
+ )
296
+ )
297
+
298
+ return adata
299
+
300
+
301
+ class pp:
302
+ @staticmethod
303
+ def make_network(
304
+ adata: ad.AnnData,
305
+ *,
306
+ sample_key: Optional[str] = None,
307
+ method: str = "knn",
308
+ cutoff: Union[int, float] = 10,
309
+ spatial_key: str = "spatial",
310
+ key_added: str = "oncoordinate_network",
311
+ inplace: bool = True,
312
+ ) -> ad.AnnData:
313
+
314
+ if not inplace:
315
+ adata = adata.copy()
316
+
317
+ if spatial_key not in adata.obsm:
318
+ raise KeyError(
319
+ f"Spatial coordinates '{spatial_key}' not found in adata.obsm"
320
+ )
321
+
322
+ coords = np.asarray(adata.obsm[spatial_key], dtype=float)
323
+ if coords.ndim != 2 or coords.shape[1] < 2:
324
+ raise ValueError(
325
+ f"Expected 2D coordinates in obsm['{spatial_key}'], got shape {coords.shape}"
326
+ )
327
+
328
+ n = adata.n_obs
329
+ neighbors_indices: List[np.ndarray] = [np.empty(0, dtype=int) for _ in range(n)]
330
+ neighbors_distances: List[np.ndarray] = [np.empty(0, dtype=float) for _ in range(n)]
331
+
332
+ def _build_for_indices(idx: np.ndarray) -> None:
333
+ sub_coords = coords[idx]
334
+ if sub_coords.shape[0] < 2:
335
+ return
336
+
337
+ if method.lower() == "knn":
338
+ k = int(cutoff)
339
+ k_eff = min(k + 1, sub_coords.shape[0])
340
+ nn = NearestNeighbors(n_neighbors=k_eff, metric="euclidean")
341
+ nn.fit(sub_coords)
342
+ dist, ind = nn.kneighbors(sub_coords)
343
+ elif method.lower() == "radius":
344
+ radius = float(cutoff)
345
+ nn = NearestNeighbors(radius=radius, metric="euclidean")
346
+ nn.fit(sub_coords)
347
+ dist_list, ind_list = nn.radius_neighbors(sub_coords)
348
+ for local_i, (d_arr, i_arr) in enumerate(zip(dist_list, ind_list)):
349
+ global_i = idx[local_i]
350
+ if i_arr.size == 0:
351
+ continue
352
+ mask_self = i_arr != local_i
353
+ neighbors_indices[global_i] = idx[i_arr[mask_self]]
354
+ neighbors_distances[global_i] = d_arr[mask_self]
355
+ return
356
+ else:
357
+ raise ValueError(f"Unsupported method '{method}'. Use 'knn' or 'radius'.")
358
+
359
+ for local_i, (d_row, i_row) in enumerate(zip(dist, ind)):
360
+ global_i = idx[local_i]
361
+ mask = i_row != local_i
362
+ neighbors_indices[global_i] = idx[i_row[mask]]
363
+ neighbors_distances[global_i] = d_row[mask]
364
+
365
+ if sample_key is not None and sample_key in adata.obs.columns:
366
+ sample_labels = adata.obs[sample_key].astype(str).values
367
+ samples = pd.unique(sample_labels)
368
+ for s in samples:
369
+ idx = np.where(sample_labels == s)[0]
370
+ if idx.size == 0:
371
+ continue
372
+ _build_for_indices(idx)
373
+ else:
374
+ _build_for_indices(np.arange(n, dtype=int))
375
+
376
+ edge_dict: Dict[tuple[int, int], float] = {}
377
+ for i in range(n):
378
+ neigh = neighbors_indices[i]
379
+ dist = neighbors_distances[i]
380
+ for j, d in zip(neigh, dist):
381
+ if i == j:
382
+ continue
383
+ a, b = (i, j) if i < j else (j, i)
384
+ if (a, b) not in edge_dict or d < edge_dict[(a, b)]:
385
+ edge_dict[(a, b)] = float(d)
386
+
387
+ if edge_dict:
388
+ edges = np.array(list(edge_dict.keys()), dtype=int)
389
+ distances = np.array(list(edge_dict.values()), dtype=float)
390
+ else:
391
+ edges = np.zeros((0, 2), dtype=int)
392
+ distances = np.zeros(0, dtype=float)
393
+
394
+ net = {
395
+ "neighbors_indices": [np.asarray(v, dtype=int) for v in neighbors_indices],
396
+ "neighbors_distances": [np.asarray(v, dtype=float) for v in neighbors_distances],
397
+ "edges": edges,
398
+ "edge_distances": distances,
399
+ "params": {
400
+ "sample_key": sample_key,
401
+ "method": method,
402
+ "cutoff": cutoff,
403
+ "spatial_key": spatial_key,
404
+ },
405
+ }
406
+
407
+ adata.uns[key_added] = net
408
+ logger.info(
409
+ f"Built spatial network with {edges.shape[0]} edges "
410
+ f"(method={method}, cutoff={cutoff})."
411
+ )
412
+ return adata
413
+
414
+
415
+ class tl:
416
+ @staticmethod
417
+ def get_c_niche(
418
+ adata: ad.AnnData,
419
+ *,
420
+ k_max: int,
421
+ celltype_key: str,
422
+ sample_key: Optional[str] = None,
423
+ network_key: str = "oncoordinate_network",
424
+ niche_key: str = "oncoordinate_c_niche",
425
+ inplace: bool = True,
426
+ ) -> ad.AnnData:
427
+
428
+ if not inplace:
429
+ adata = adata.copy()
430
+
431
+ if network_key not in adata.uns:
432
+ raise KeyError(
433
+ f"Network key '{network_key}' not found in adata.uns. "
434
+ "Run PP.make_network(...) first."
435
+ )
436
+
437
+ net = adata.uns[network_key]
438
+ neighbors_indices = net.get("neighbors_indices")
439
+ if neighbors_indices is None or len(neighbors_indices) != adata.n_obs:
440
+ raise ValueError(
441
+ f"Invalid neighbors_indices under uns['{network_key}']; "
442
+ "expected list of length n_obs."
443
+ )
444
+
445
+ if celltype_key not in adata.obs.columns:
446
+ raise KeyError(f"celltype_key '{celltype_key}' not found in adata.obs")
447
+
448
+ celltypes = adata.obs[celltype_key].astype(str).values
449
+ unique_ct = np.unique(celltypes)
450
+ n_cells = adata.n_obs
451
+ n_ct = unique_ct.size
452
+ ct_to_idx = {ct: i for i, ct in enumerate(unique_ct)}
453
+
454
+ feats = np.zeros((n_cells, n_ct), dtype=float)
455
+ for i in range(n_cells):
456
+ neigh = np.asarray(neighbors_indices[i], dtype=int)
457
+ if neigh.size == 0:
458
+ continue
459
+ neigh_ct = celltypes[neigh]
460
+ indices = [ct_to_idx[c] for c in neigh_ct]
461
+ counts = np.bincount(indices, minlength=n_ct)
462
+ total = counts.sum()
463
+ if total > 0:
464
+ feats[i, :] = counts / total
465
+
466
+ kmeans = KMeans(
467
+ n_clusters=int(k_max),
468
+ random_state=0,
469
+ n_init=10,
470
+ )
471
+
472
+ labels = kmeans.fit_predict(feats)
473
+ adata.obs[niche_key] = pd.Categorical(labels.astype(str))
474
+
475
+ comp_ct = pd.DataFrame(
476
+ 0.0,
477
+ index=np.arange(k_max),
478
+ columns=unique_ct,
479
+ )
480
+ for k in range(k_max):
481
+ mask = labels == k
482
+ if not np.any(mask):
483
+ continue
484
+ comp_ct.iloc[k, :] = feats[mask].mean(axis=0)
485
+
486
+ comp_sample = None
487
+ if sample_key is not None and sample_key in adata.obs.columns:
488
+ comp_sample = pd.crosstab(
489
+ adata.obs[sample_key],
490
+ adata.obs[niche_key],
491
+ normalize="index",
492
+ )
493
+
494
+ net["c_niche_centers"] = kmeans.cluster_centers_
495
+ net["c_niche_celltype_composition"] = comp_ct
496
+ if comp_sample is not None:
497
+ net["c_niche_sample_composition"] = comp_sample
498
+ net.setdefault("c_niche_params", {})
499
+ net["c_niche_params"].update(
500
+ {
501
+ "niche_key": niche_key,
502
+ "celltype_key": celltype_key,
503
+ "sample_key": sample_key,
504
+ "k_max": int(k_max),
505
+ }
506
+ )
507
+ adata.uns[network_key] = net
508
+
509
+ logger.info(
510
+ f"Computed C-niches ({k_max} clusters) into obs['{niche_key}'] "
511
+ f"using celltype_key='{celltype_key}'."
512
+ )
513
+ return adata
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: oncoordinate
3
+ Version: 0.1.7
4
+ Summary: Oncoordinate is an interpretable deep learning framework for single-cell and spatial transcriptomic analysis of malignancy that learns malignant and malignancy-associated cell states across epithelial, stromal, and immune lineages while remaining tightly integrated with the scverse ecosystem.
5
+ Author-email: "Vignesh V. Venkat" <vvv11@scarletmail.rutgers.edu>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Vignesh Venkat
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/Viggyvenkat/Oncoordinate
29
+ Project-URL: Issues, https://github.com/Viggyvenkat/Oncoordinate/issues
30
+ Keywords: cancer,pathways,bioinformatics
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
36
+ Requires-Python: >=3.10
37
+ Description-Content-Type: text/markdown
38
+ License-File: LICENSE
39
+ Requires-Dist: anndata
40
+ Requires-Dist: numpy
41
+ Requires-Dist: pandas
42
+ Requires-Dist: scipy
43
+ Requires-Dist: scanpy
44
+ Requires-Dist: scvi-tools
45
+ Requires-Dist: scikit-learn==1.7.2
46
+ Requires-Dist: joblib
47
+ Requires-Dist: umap-learn
48
+ Requires-Dist: gseapy
49
+ Requires-Dist: matplotlib
50
+ Requires-Dist: seaborn
51
+ Requires-Dist: python-igraph
52
+ Requires-Dist: pytorch-tabnet
53
+ Requires-Dist: ipywidgets
54
+ Requires-Dist: imageio
55
+ Requires-Dist: click
56
+ Dynamic: license-file
57
+
58
+ # Oncoordinate
59
+
60
+ ### Contents
61
+ 1. Introduction
62
+ 2. Discovering single-cell states
63
+ 3. Projecting to spatial transcriptomic cohorts
64
+ 4. References
65
+ 5. Acknowledgements
66
+
67
+ Note: we provide a demo notebook (```vignettes/tutorial.html```) and two spatial datasets (```demo-data/V10U24-037_A```, ```demo-data/V10U24-037_B```). We will be uploading the train set, the test set, and the reference_sc.h5ad on zenodo. We have also provided an `environment.yml` file so that you can run the command: `conda env create -f environment.yml` and get a working environment for Oncoordinate.
68
+
69
+ ## 1. Introduction
70
+ Oncoordinate is an interpretable deep learning framework for single-cell and spatial transcriptomic analysis of malignancy that learns malignant and malignancy-associated cell states across epithelial, stromal, and immune lineages while remaining tightly integrated with the scverse ecosystem. Built on a sequential attention–based and trained on an integrated lung atlas (~3M cells spanning normal, chronic disease, and multiple lung carcinoma subtypes), Oncoordinate predicts a four-stage neoplastic continuum (normal, dysplastic, pre-malignant, malignant) with calibrated probabilities and sparse, step-wise feature selection that can be traced back to genes and pathways. Beyond single-cell analysis, it includes a de novo label transfer pipeline based on scVI and scANVI that projects these learned states into spatial transcriptomic datasets (e.g., 10x Visium) via pseudospots and joint latent embeddings, enabling the localization of aggressive niches, tumor–CAF neighborhoods, and other malignant ecosystems within intact tissue. Oncoordinate also works with niche detection methods such as SOAPy and works seamlessly with AnnData and Scanpy-based workflows, providing a GPU-accelerated, atlas-scale, and plug-and-play framework for malignancy modeling across molecular and spatial dimensions.
71
+
72
+ To install Oncoordinate, please run:
73
+
74
+ ``` pip install oncoordinate ```
75
+
76
+ ![Fig1](figures/Figure%201/Slide1.png)
77
+
78
+ ## 2. Discovering single-cell states
79
+ Oncoordinate discovers malignant and malignancy-associated cell states by modeling neoplastic progression as a continuous, lineage-aware process rather than a binary tumor versus non-tumor classification. Using atlas-scale single-cell RNA-seq data, the model integrates gene-level features and pathway scores to learn a four-stage neoplastic continuum spanning normal, dysplastic, pre-malignant, and malignant states. Its sequential attention mechanism performs sparse, step-wise feature selection, allowing the model to focus on distinct transcriptional programs at different stages of malignancy while preserving interpretability. This design enables Oncoordinate to recover coherent oncogenic trajectories within individual lineages, such as epithelial, fibroblast, immune, and endothelial compartments, and to quantify heterogeneity both within and across patients. The resulting per-cell malignancy probabilities and lineage-resolved scores provide an interpretable representation of tumor ecosystem remodeling that can be directly used for downstream analyses, including trajectory visualization, pathway interrogation, and patient-level stratification.
80
+
81
+ ![Fig2](figures/Figure%202/Slide1.png)
82
+
83
+ ## 3. Projecting to spatial transcriptomic cohorts
84
+ To translate single-cell–derived malignancy states into intact tissue architecture, Oncoordinate implements a de novo label transfer framework tailored for spatial transcriptomics data. Single-cell profiles sharing the same lineage and malignancy state are aggregated into pseudospots to approximate the multicellular composition of spatial capture spots, and these pseudospots are jointly embedded with spatial transcriptomic data using scVI to learn a shared, batch-corrected latent space. scANVI is then applied in a semi-supervised manner to infer probabilistic malignancy and lineage labels for each spatial spot. This approach enables robust projection of malignant programs into spatial coordinates, revealing colocalized malignant neighborhoods such as tumor–fibroblast (CAF) niches, regions of stromal remodeling, and immune-associated malignant ecosystems. By combining probabilistic spatial labeling with downstream niche detection methods such as SOAPy, Oncoordinate allows users to identify and characterize aggressive, spatially confined microenvironments that are not apparent from dissociated single-cell data alone.
85
+
86
+ ![Fig3](figures/Figure%203/Slide1.png)
87
+
88
+ ## 4. References
89
+ - Venkat V. et al. Disruptive changes in tissue microenvironment prime oncogenic processes at different stages of carcinogenesis in lung. bioRxiv (2024).
90
+ - Venkat VV, De S. Oncoordinate-derived single-cell states translate to malignant clusters in spatial transcriptomic cohorts. bioRxiv (2026).
91
+
92
+ ## 5. Acknowledgements
93
+ We thank colleagues at the Rutgers Cancer Institute and members of the De Laboratory for their guidance and support.
@@ -0,0 +1,62 @@
1
+ oncoordinate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ oncoordinate/lt.py,sha256=pmdF3TYundHe5qSY52VvnKpu-wOWGmaKMbYD4JnsOj0,15887
3
+ oncoordinate/oncoordinate.joblib,sha256=OwN7FmRyRCV-WtaH962vtGbq-jLI4vgcscqIGI6wa4M,1937271
4
+ oncoordinate/sc.py,sha256=5OHrHMx0FSugyI-BpjaPcmExXplArhtqWvIihnsDI2w,24754
5
+ oncoordinate/sp.py,sha256=YksjUB72TbLqwUm0A1fLYF3kqYWhu15sFCc6L0yGxY4,16672
6
+ oncoordinate/HallmarkPathGMT/HALLMARK_ADIPOGENESIS.v2023.2.Hs.gmt,sha256=3wE_zDwqggGdvhvpCqzBEexTpSQnO6zyAf0iY2jc56Q,1283
7
+ oncoordinate/HallmarkPathGMT/HALLMARK_ALLOGRAFT_REJECTION.v2023.2.Hs.gmt,sha256=tBGJPLLWCO01szrGUbfB-iupXWQqMcJqEQnWn5W0bnM,1222
8
+ oncoordinate/HallmarkPathGMT/HALLMARK_ANDROGEN_RESPONSE.v2023.2.Hs.gmt,sha256=eWMP5touZLL2H0sp9TRc733X6XA04QBJONi5zQkN6os,727
9
+ oncoordinate/HallmarkPathGMT/HALLMARK_ANGIOGENESIS.v2023.2.Hs.gmt,sha256=eF_lbvQKTbuhh_5Uq0_pO6utU18fjRqI9AV16_SV7_I,304
10
+ oncoordinate/HallmarkPathGMT/HALLMARK_APICAL_JUNCTION.v2023.2.Hs.gmt,sha256=GgKGajGsLRwcwm8fZKWiehRGUQZbcg4zX8nuZBiuDKg,1285
11
+ oncoordinate/HallmarkPathGMT/HALLMARK_APICAL_SURFACE.v2023.2.Hs.gmt,sha256=LQrrCiAf5goHbPrn9D0n0ccOUT35HQN7PGA6vM3qvr0,380
12
+ oncoordinate/HallmarkPathGMT/HALLMARK_APOPTOSIS.v2023.2.Hs.gmt,sha256=yNTbYbpCFsY9vgIQQUM4Y2df_bBPxXC9zJKy0OsuS9g,1001
13
+ oncoordinate/HallmarkPathGMT/HALLMARK_BILE_ACID_METABOLISM.v2023.2.Hs.gmt,sha256=ve5P3rZ89nhrn453L6SFJHEuWu552jbME_wnQX-b9s8,801
14
+ oncoordinate/HallmarkPathGMT/HALLMARK_CHOLESTEROL_HOMEOSTASIS.v2023.2.Hs.gmt,sha256=hSnwHNVJFecAYE-u-KKIch9xrbulB6jFZrVaXh5tBW4,553
15
+ oncoordinate/HallmarkPathGMT/HALLMARK_COAGULATION.v2023.2.Hs.gmt,sha256=t5yV_Nl-d9ddpeVJgKOyV1vCLgP8MY0SUBJSy0hNF-o,823
16
+ oncoordinate/HallmarkPathGMT/HALLMARK_COMPLEMENT.v2023.2.Hs.gmt,sha256=Sd_ZGCrCX-46yC-2wAVH4Y36HA0fZnZ9wpivdfqeITQ,1212
17
+ oncoordinate/HallmarkPathGMT/HALLMARK_DNA_REPAIR.v2023.2.Hs.gmt,sha256=qO-LwjlVR-j5evTCuDE-wSJuBzkGzPdYLgWqcp8GjuU,974
18
+ oncoordinate/HallmarkPathGMT/HALLMARK_E2F_TARGETS.v2023.2.Hs.gmt,sha256=jtjrIQRqfisWWuztAYNAtJJHJRTbW3icsXUqRBkKeOY,1263
19
+ oncoordinate/HallmarkPathGMT/HALLMARK_EPITHELIAL_MESENCHYMAL_TRANSITION.v2023.2.Hs.gmt,sha256=p1gmkTx8J8rHOLl2J_qwzY4FGPPiw4mSv09mdIcr8EQ,1306
20
+ oncoordinate/HallmarkPathGMT/HALLMARK_ESTROGEN_RESPONSE_EARLY.v2023.2.Hs.gmt,sha256=FUR8Mu434I_fDdDmFwErYYF2oEYwMjRiSXTFitCND1U,1335
21
+ oncoordinate/HallmarkPathGMT/HALLMARK_ESTROGEN_RESPONSE_LATE.v2023.2.Hs.gmt,sha256=lX9ptgQD_Jab1RXLYHJMpALNbbxmI_xImmq09gO87-U,1310
22
+ oncoordinate/HallmarkPathGMT/HALLMARK_FATTY_ACID_METABOLISM.v2023.2.Hs.gmt,sha256=04JidJvr6YTv3nGuHik5VfvS-AuQnaCqlw3rK_w7S3U,1046
23
+ oncoordinate/HallmarkPathGMT/HALLMARK_G2M_CHECKPOINT.v2023.2.Hs.gmt,sha256=-QgDI7jxLcgSIuvE-TpvjihxSgUbmFXNXyB2aiP0_rM,1266
24
+ oncoordinate/HallmarkPathGMT/HALLMARK_GLYCOLYSIS.v2023.2.Hs.gmt,sha256=VYxq25EZzKWzNlTS1RWSVKsa4q7DV4Qbe3fYc_gD0C0,1241
25
+ oncoordinate/HallmarkPathGMT/HALLMARK_HEDGEHOG_SIGNALING.v2023.2.Hs.gmt,sha256=-d5ChLz4-j-rYcIPNz6uhs6zR0cUT2Qr3ILFQVFoSYo,306
26
+ oncoordinate/HallmarkPathGMT/HALLMARK_HEME_METABOLISM.v2023.2.Hs.gmt,sha256=Qtqp5sa5v6xmI6ZUjw3zNZR-N4jznvePrPGPaaIp130,1292
27
+ oncoordinate/HallmarkPathGMT/HALLMARK_HYPOXIA.v2023.2.Hs.gmt,sha256=h47PZ24OueXoKazsrL73gvwV6ilbxzhBJ7n3wGOjHVY,1244
28
+ oncoordinate/HallmarkPathGMT/HALLMARK_IL2_STAT5_SIGNALING.v2023.2.Hs.gmt,sha256=RQ4Gfv-v7rQIwvKUoTRJV9hH3RqenbcDu6v9wdaA22E,1313
29
+ oncoordinate/HallmarkPathGMT/HALLMARK_IL6_JAK_STAT3_SIGNALING.v2023.2.Hs.gmt,sha256=SMDisUR_aj252IItNg38YGnw2gQ5x-3pEORN3eD37MQ,634
30
+ oncoordinate/HallmarkPathGMT/HALLMARK_INFLAMMATORY_RESPONSE.v2023.2.Hs.gmt,sha256=DL2WB-uH6OBOx8oi0ZhROJYQ7acWexVrmZKN0JE03dA,1291
31
+ oncoordinate/HallmarkPathGMT/HALLMARK_INTERFERON_ALPHA_RESPONSE.v2023.2.Hs.gmt,sha256=2D14ED_HPsI67ovxny9-KPcr56CVrY1JNFzRGehQwGs,697
32
+ oncoordinate/HallmarkPathGMT/HALLMARK_INTERFERON_GAMMA_RESPONSE.v2023.2.Hs.gmt,sha256=h9fftiHUlB1vnuqe_rRWhy27cp7NzxP78s4-vzUDHjI,1318
33
+ oncoordinate/HallmarkPathGMT/HALLMARK_KRAS_SIGNALING_DN.v2023.2.Hs.gmt,sha256=jJgWqogXXDPcB1xXZQ5m0RaQVUaM1yZNHdJq480o6xs,1326
34
+ oncoordinate/HallmarkPathGMT/HALLMARK_KRAS_SIGNALING_UP.v2023.2.Hs.gmt,sha256=hqyOBFaRsCE1gxTVqQP6kOdlDciBTsnTo2znimgAB5o,1314
35
+ oncoordinate/HallmarkPathGMT/HALLMARK_MITOTIC_SPINDLE.v2023.2.Hs.gmt,sha256=d7zosxuYcFlS26lQbG8W9j4P2qARchE7HgCDcxwOLgs,1350
36
+ oncoordinate/HallmarkPathGMT/HALLMARK_MTORC1_SIGNALING.v2023.2.Hs.gmt,sha256=q9Yc2fxXWjWapGal6bNXdc7w_fv6e8xl0H3kmaRlAEk,1286
37
+ oncoordinate/HallmarkPathGMT/HALLMARK_MYC_TARGETS_V1.v2023.2.Hs.gmt,sha256=8se5j-hWXstttTZr_dbHciOCKwchVxvTqFZ8SL2cxDw,1287
38
+ oncoordinate/HallmarkPathGMT/HALLMARK_MYC_TARGETS_V2.v2023.2.Hs.gmt,sha256=Jh1sL1Xpa28bEh4leZE-Z4_4iYr2kBQSsok7Bfxo4eA,440
39
+ oncoordinate/HallmarkPathGMT/HALLMARK_MYOGENESIS.v2023.2.Hs.gmt,sha256=hQsMkzX5GPu-GI63Pyf8asOrLEm00JUb-sCOLxP0vhU,1250
40
+ oncoordinate/HallmarkPathGMT/HALLMARK_NOTCH_SIGNALING.v2023.2.Hs.gmt,sha256=xOf5dS4fdq8H3POw2UsXtY3-SZJm03p8gTCNLRPWCLs,288
41
+ oncoordinate/HallmarkPathGMT/HALLMARK_OXIDATIVE_PHOSPHORYLATION.v2023.2.Hs.gmt,sha256=OssD_puZLztFcJ_7wbigrno9UOEO2PUFwWRVLUJZeqo,1406
42
+ oncoordinate/HallmarkPathGMT/HALLMARK_P53_PATHWAY.v2023.2.Hs.gmt,sha256=KyDt3T5irP0ccO66y_FaRmyZGVYEMJ36FVEvkT7-m4U,1291
43
+ oncoordinate/HallmarkPathGMT/HALLMARK_PANCREAS_BETA_CELLS.v2023.2.Hs.gmt,sha256=F91XHWhgKP_X6UFcPoV4vFV2o8JlkZTEty-XkdG_xBo,333
44
+ oncoordinate/HallmarkPathGMT/HALLMARK_PEROXISOME.v2023.2.Hs.gmt,sha256=eHaOXJIkcQXTZCXkEDbX3LXWVpSgQqFRUcVJnEyNbWY,730
45
+ oncoordinate/HallmarkPathGMT/HALLMARK_PI3K_AKT_MTOR_SIGNALING.v2023.2.Hs.gmt,sha256=KDE1SFIBWGrgAbt1hhO-1sAXXSxz2ZUYMFSW4_EJgms,750
46
+ oncoordinate/HallmarkPathGMT/HALLMARK_PROTEIN_SECRETION.v2023.2.Hs.gmt,sha256=5fvCdcYGAPXqIKpyeaHzGrIaeG3nRwGAGclAHcIqu4g,677
47
+ oncoordinate/HallmarkPathGMT/HALLMARK_REACTIVE_OXYGEN_SPECIES_PATHWAY.v2023.2.Hs.gmt,sha256=-OdEhAHiTkiVkuN2KAnJs6qc8ddM-zqIhuqzJ0Z4fsQ,411
48
+ oncoordinate/HallmarkPathGMT/HALLMARK_SPERMATOGENESIS.v2023.2.Hs.gmt,sha256=n9Y0N2kZDUkPsG0YehKgHYV1aCaIDA6mv3wfMEIysQQ,891
49
+ oncoordinate/HallmarkPathGMT/HALLMARK_TGF_BETA_SIGNALING.v2023.2.Hs.gmt,sha256=w2tNLtHZOlzhWlZ7AKeksnEtOxZ0SwxdDpKdUs8QI28,434
50
+ oncoordinate/HallmarkPathGMT/HALLMARK_TNFA_SIGNALING_VIA_NFKB.v2023.2.Hs.gmt,sha256=obljegRl5_LtnUgilBgqAFW5_EouaF1LojvBYSFBMmc,1294
51
+ oncoordinate/HallmarkPathGMT/HALLMARK_UNFOLDED_PROTEIN_RESPONSE.v2023.2.Hs.gmt,sha256=leYvr_WLTlgJoJoxB4VOnfKP3dmyE39WITs7Mbx4ga0,816
52
+ oncoordinate/HallmarkPathGMT/HALLMARK_UV_RESPONSE_DN.v2023.2.Hs.gmt,sha256=D3_Tmkku4JzXBPgv2lquw03Ty7eWgXxDrqD2mXBiFi4,971
53
+ oncoordinate/HallmarkPathGMT/HALLMARK_UV_RESPONSE_UP.v2023.2.Hs.gmt,sha256=8NGFUQ4UrdR_ep6FBSNM7jvT3xDHbGehZc0J0nEqJUA,1025
54
+ oncoordinate/HallmarkPathGMT/HALLMARK_WNT_BETA_CATENIN_SIGNALING.v2023.2.Hs.gmt,sha256=B-U77KUw5P2NPjnNEbGU8Fur_pc1PY6p7IZtOtzL7tU,361
55
+ oncoordinate/HallmarkPathGMT/HALLMARK_XENOBIOTIC_METABOLISM.v2023.2.Hs.gmt,sha256=MNVFUjU7LLn-sja6U72I6va351gC_p-p_LbET2bGCgY,1282
56
+ oncoordinate/HallmarkPathGMT/REACTOME_CELL_CYCLE.v2023.2.Hs.gmt,sha256=gBVSh4U-W1tlQnCsS-t82gH6rvqULIGM9xPupNmMhIU,4282
57
+ oncoordinate/HallmarkPathGMT/REACTOME_SIGNALING_BY_EGFR_IN_CANCER.v2023.2.Hs.gmt,sha256=0zUC0sfyFOG72yuwQou-VicZXoCA5My0SsDzWZM1osA,261
58
+ oncoordinate-0.1.7.dist-info/licenses/LICENSE,sha256=wLtNslPiZu4XVG7WaL9Sw4cO9tkXWaOrXBH8kEp7YKU,1071
59
+ oncoordinate-0.1.7.dist-info/METADATA,sha256=Gg8CwTmNNOBhZ9ZEPqYFqdMB6ULOQU6sCwj1gIsrBTU,7570
60
+ oncoordinate-0.1.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
61
+ oncoordinate-0.1.7.dist-info/top_level.txt,sha256=f_sVgWqAiEbx12sQ1Lc8EbfXykZM9ANKVOMl8cUhOEI,13
62
+ oncoordinate-0.1.7.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+