bsvae 0.4.0__tar.gz → 0.4.2__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.
- {bsvae-0.4.0 → bsvae-0.4.2}/PKG-INFO +1 -1
- {bsvae-0.4.0 → bsvae-0.4.2}/pyproject.toml +1 -1
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/cli/networks.py +5 -1
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/networks/targets.py +64 -19
- {bsvae-0.4.0 → bsvae-0.4.2}/LICENSE +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/README.md +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/__init__.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/benchmark/__init__.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/benchmark/acorde.R +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/benchmark/edges.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/benchmark/harness.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/benchmark/methods.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/benchmark/runners.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/benchmark/twn.R +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/benchmark/wgcna.R +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/cli/__init__.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/cli/_common.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/cli/benchmark.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/cli/simulate.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/cli/sweep_k.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/cli/train.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/latent/__init__.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/latent/latent_analysis.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/latent/latent_export.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/models/__init__.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/models/decoder.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/models/encoder.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/models/gmm_prior.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/models/gmvae.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/models/losses.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/models/vae.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/networks/__init__.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/networks/extract_networks.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/networks/module_extraction.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/networks/postprocess.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/networks/quality.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/networks/utils.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/simulation/__init__.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/simulation/generate.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/simulation/isoform.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/simulation/metrics.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/simulation/scenario.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/utils/__init__.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/utils/datasets.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/utils/helpers.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/utils/hierarchy.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/utils/isoform.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/utils/modelIO.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/utils/preprocess.py +0 -0
- {bsvae-0.4.0 → bsvae-0.4.2}/src/bsvae/utils/training.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "bsvae"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.2" # dynamically resolved by poetry-dynamic-versioning
|
|
4
4
|
description = "Gaussian Mixture VAE for multi-modal biological module discovery in omics data."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = ["Kynon J Benjamin <kj.benjamin90@gmail.com>"]
|
|
@@ -211,6 +211,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
211
211
|
tg.add_argument("--covariates", default=None, help="Sample table (samples × columns) for --condition.")
|
|
212
212
|
tg.add_argument("--condition", default=None, help="Two-level column of --covariates for a case/control interaction test.")
|
|
213
213
|
tg.add_argument("--n-neighbours", type=int, default=20)
|
|
214
|
+
tg.add_argument("--max-target-corr", type=float, default=None,
|
|
215
|
+
help="Exclude as targets the modules whose eigengene correlates with the gene's own module above "
|
|
216
|
+
"this, in the observed statistic and in the null (default: none; 0.4 removes split components "
|
|
217
|
+
"of one programme, as for isoform-specific).")
|
|
214
218
|
tg.add_argument("--no-residualize-eigengenes", action="store_true", default=False)
|
|
215
219
|
tg.add_argument("--no-leave-one-out", action="store_true", default=False)
|
|
216
220
|
tg.add_argument("--keep-tx-version", action="store_true", default=False)
|
|
@@ -646,7 +650,7 @@ def handle_target_genes(args, logger: logging.Logger) -> None:
|
|
|
646
650
|
expr, tx2gene, labels, fids, targets, null=args.null, n_perm=args.n_perm, n_matched=args.n_matched,
|
|
647
651
|
min_kme=args.min_kme, alpha=args.alpha, counts=counts, condition=condition, n_neighbours=args.n_neighbours,
|
|
648
652
|
residualize_eigengenes=not args.no_residualize_eigengenes, leave_one_out=not args.no_leave_one_out,
|
|
649
|
-
keep_version=args.keep_tx_version, seed=args.seed,
|
|
653
|
+
keep_version=args.keep_tx_version, seed=args.seed, max_target_corr=args.max_target_corr,
|
|
650
654
|
)
|
|
651
655
|
res["isoforms"].to_csv(out_dir / "target_isoforms.tsv", sep="\t")
|
|
652
656
|
res["genes"].to_csv(out_dir / "target_genes.tsv", sep="\t")
|
|
@@ -195,10 +195,13 @@ def resolve_target_transcripts(
|
|
|
195
195
|
Transcripts of the target genes (plus explicitly listed transcripts) that
|
|
196
196
|
are present in ``tx_gene`` (feature_id → gene_id for the expression
|
|
197
197
|
matrix). Gene symbols are resolved through ``annotation`` when given.
|
|
198
|
-
Returns (feature_id → gene_id for the targets, list of unmatched inputs)
|
|
198
|
+
Returns (feature_id → gene_id for the targets, list of unmatched inputs);
|
|
199
|
+
the index keeps the matrix's own feature IDs (versions included), while
|
|
200
|
+
listed transcripts match with versions stripped unless ``keep_version``.
|
|
199
201
|
"""
|
|
200
202
|
tx_gene = tx_gene.copy()
|
|
201
|
-
tx_gene.index =
|
|
203
|
+
tx_gene.index = tx_gene.index.astype(str)
|
|
204
|
+
by_id = {strip_feature_version(f, keep_version): f for f in tx_gene.index}
|
|
202
205
|
unmatched: List[str] = []
|
|
203
206
|
target_genes: set = set()
|
|
204
207
|
if genes:
|
|
@@ -217,8 +220,8 @@ def resolve_target_transcripts(
|
|
|
217
220
|
sel = tx_gene[tx_gene.isin(target_genes)] if target_genes else tx_gene.iloc[0:0]
|
|
218
221
|
if transcripts:
|
|
219
222
|
ids = [strip_feature_version(t, keep_version) for t in transcripts]
|
|
220
|
-
present = [t for t in ids if t in
|
|
221
|
-
unmatched.extend([t for t, i in zip(transcripts, ids) if i not in
|
|
223
|
+
present = [by_id[t] for t in ids if t in by_id]
|
|
224
|
+
unmatched.extend([t for t, i in zip(transcripts, ids) if i not in by_id])
|
|
222
225
|
sel = pd.concat([sel, tx_gene.loc[present]])
|
|
223
226
|
sel = sel[~sel.index.duplicated()]
|
|
224
227
|
return sel, unmatched
|
|
@@ -300,6 +303,7 @@ def target_gene_tests(
|
|
|
300
303
|
leave_one_out: bool = True,
|
|
301
304
|
keep_version: bool = False,
|
|
302
305
|
seed: int = 0,
|
|
306
|
+
max_target_corr: Optional[float] = None,
|
|
303
307
|
) -> Dict[str, pd.DataFrame]:
|
|
304
308
|
"""
|
|
305
309
|
Test every isoform of the target genes for membership of a module other
|
|
@@ -327,6 +331,17 @@ def target_gene_tests(
|
|
|
327
331
|
Two-level sample grouping (index = sample IDs); the best-other kME is
|
|
328
332
|
then also estimated per group and the difference tested with
|
|
329
333
|
Fisher's z (``p_interaction``).
|
|
334
|
+
max_target_corr
|
|
335
|
+
Exclude as targets the modules whose eigengene correlates with the
|
|
336
|
+
gene's own module eigengene above this (absolute Pearson correlation
|
|
337
|
+
of the eigengenes, as in
|
|
338
|
+
:func:`bsvae.utils.isoform.isoform_specific_calls`). The exclusion
|
|
339
|
+
is applied before the best module is chosen and inside every null
|
|
340
|
+
(the permutation maximum, the matched pool's best-other |kME| and the
|
|
341
|
+
analytic Bonferroni factor), so the test compares like with like.
|
|
342
|
+
``n_targets`` reports the eligible modules per isoform; an isoform
|
|
343
|
+
with none gets ``best_other_module = -1``, ``kme_residual = 0`` and
|
|
344
|
+
``p_value = 1``. Default ``None`` (no exclusion).
|
|
330
345
|
|
|
331
346
|
min_kme, alpha
|
|
332
347
|
Effect-size floor and BH FDR within the target set. The floor is
|
|
@@ -399,27 +414,56 @@ def target_gene_tests(
|
|
|
399
414
|
K[rows_idx] = Zrows[rows_idx] @ Eg / (S - 1)
|
|
400
415
|
return K
|
|
401
416
|
|
|
417
|
+
# eligible target modules per gene module: never the gene's own module and, with
|
|
418
|
+
# max_target_corr, never a module whose eigengene tracks the gene's module (a split
|
|
419
|
+
# component of the same programme rather than a divergence target)
|
|
420
|
+
Ecorr = None
|
|
421
|
+
if max_target_corr is not None and len(cols) > 1:
|
|
422
|
+
Ecorr = np.atleast_2d(np.nan_to_num(np.corrcoef(eigengenes.values.T.astype(np.float64))))
|
|
423
|
+
|
|
424
|
+
def eligible(g: int) -> np.ndarray:
|
|
425
|
+
ok = np.ones(len(cols), bool)
|
|
426
|
+
if g in cols:
|
|
427
|
+
j = cols.index(int(g))
|
|
428
|
+
ok[j] = False
|
|
429
|
+
if Ecorr is not None:
|
|
430
|
+
ok &= np.abs(Ecorr[j]) <= float(max_target_corr)
|
|
431
|
+
ok[j] = False
|
|
432
|
+
return ok
|
|
433
|
+
|
|
434
|
+
elig = {int(g): eligible(int(g)) for g in np.unique(gm)}
|
|
435
|
+
|
|
436
|
+
def mask_other(Kmat: np.ndarray, gmods: np.ndarray) -> np.ndarray:
|
|
437
|
+
Km = Kmat.copy()
|
|
438
|
+
for i, g in enumerate(gmods):
|
|
439
|
+
ok = elig.get(int(g))
|
|
440
|
+
if ok is None:
|
|
441
|
+
ok = elig[int(g)] = eligible(int(g))
|
|
442
|
+
Km[i, ~ok] = 0.0
|
|
443
|
+
return Km
|
|
444
|
+
|
|
402
445
|
K = kme_matrix(Z, gm)
|
|
403
446
|
kme_own = np.array([K[i, cols.index(int(g))] if g in cols else np.nan for i, g in enumerate(gm)])
|
|
404
|
-
K_other = K
|
|
405
|
-
|
|
406
|
-
if g in cols:
|
|
407
|
-
K_other[i, cols.index(int(g))] = 0.0
|
|
447
|
+
K_other = mask_other(K, gm)
|
|
448
|
+
n_targets = np.array([int(elig[int(g)].sum()) for g in gm])
|
|
408
449
|
best_j = np.abs(K_other).argmax(axis=1)
|
|
409
450
|
best = K_other[np.arange(len(targets)), best_j]
|
|
451
|
+
no_target = n_targets == 0
|
|
410
452
|
|
|
411
453
|
# null distribution of the best-other |kME|
|
|
412
454
|
if null == "analytic":
|
|
413
455
|
from scipy import stats as sps
|
|
414
456
|
rr = np.minimum(best ** 2, 1 - 1e-12)
|
|
415
457
|
tval = np.abs(best) * np.sqrt(max(S - 2, 1) / (1 - rr))
|
|
416
|
-
p = np.minimum(2.0 * sps.t.sf(tval, max(S - 2, 1)) *
|
|
458
|
+
p = np.minimum(2.0 * sps.t.sf(tval, max(S - 2, 1)) * np.maximum(n_targets, 1), 1.0)
|
|
417
459
|
elif null == "permutation":
|
|
418
|
-
p = np.
|
|
460
|
+
p = np.ones(len(targets))
|
|
419
461
|
for g in np.unique(gm):
|
|
420
462
|
rows_idx = np.where(gm == g)[0]
|
|
421
463
|
Eg = _standardize_cols(_residualize_eigengenes(eigengenes, cols.index(int(g))).values) if (residualize_eigengenes and g in cols) else E
|
|
422
|
-
other = [
|
|
464
|
+
other = np.where(elig[int(g)])[0]
|
|
465
|
+
if other.size == 0:
|
|
466
|
+
continue
|
|
423
467
|
for i in rows_idx:
|
|
424
468
|
perms = np.array([rng.permutation(S) for _ in range(int(n_perm))])
|
|
425
469
|
null_k = (Z[i][perms] @ Eg[:, other]) / (S - 1)
|
|
@@ -437,10 +481,7 @@ def target_gene_tests(
|
|
|
437
481
|
pool_feat = np.column_stack([pool_expr.mean(axis=1).values, (pool_expr.values == 0).mean(axis=1)])
|
|
438
482
|
pool_Z = _zscore_rows(resid_all.loc[pool].values)
|
|
439
483
|
pool_gm = np.array([gene_mod.get(g, -1) for g in stats_all.loc[pool, "gene_id"].values])
|
|
440
|
-
pool_K = kme_matrix(pool_Z, pool_gm)
|
|
441
|
-
for i, g in enumerate(pool_gm):
|
|
442
|
-
if g in cols:
|
|
443
|
-
pool_K[i, cols.index(int(g))] = 0.0
|
|
484
|
+
pool_K = mask_other(kme_matrix(pool_Z, pool_gm), pool_gm)
|
|
444
485
|
pool_best = np.abs(pool_K).max(axis=1)
|
|
445
486
|
tgt_expr = expr.reindex(targets.index)
|
|
446
487
|
tgt_feat = np.column_stack([tgt_expr.mean(axis=1).values, (tgt_expr.values == 0).mean(axis=1)])
|
|
@@ -450,8 +491,10 @@ def target_gene_tests(
|
|
|
450
491
|
d = (((pool_feat - tgt_feat[i]) / scale) ** 2).sum(axis=1)
|
|
451
492
|
near = np.argsort(d, kind="stable")[: int(n_matched)]
|
|
452
493
|
p[i] = (1.0 + (pool_best[near] >= abs(best[i])).sum()) / (len(near) + 1.0)
|
|
494
|
+
p = np.where(no_target, 1.0, p)
|
|
495
|
+
best = np.where(no_target, 0.0, best)
|
|
453
496
|
q = _bh_adjust(p)
|
|
454
|
-
called = (np.abs(best) >= float(min_kme)) & (q < float(alpha))
|
|
497
|
+
called = (np.abs(best) >= float(min_kme)) & (q < float(alpha)) & ~no_target
|
|
455
498
|
|
|
456
499
|
out = pd.DataFrame({
|
|
457
500
|
"gene_id": targets.values,
|
|
@@ -463,7 +506,8 @@ def target_gene_tests(
|
|
|
463
506
|
"isoform_module": own,
|
|
464
507
|
"label_discordant": (own >= 0) & (gm >= 0) & (own != gm),
|
|
465
508
|
"kme_gene_module": kme_own,
|
|
466
|
-
"
|
|
509
|
+
"n_targets": n_targets,
|
|
510
|
+
"best_other_module": [-1 if nt else cols[j] for j, nt in zip(best_j, no_target)],
|
|
467
511
|
"kme_residual": best,
|
|
468
512
|
"p_value": p,
|
|
469
513
|
"q_value": q,
|
|
@@ -498,9 +542,10 @@ def target_gene_tests(
|
|
|
498
542
|
n1, n2 = masks[0].sum(), masks[1].sum()
|
|
499
543
|
z1, z2 = np.arctanh(np.clip(k_grp[0], -0.999, 0.999)), np.arctanh(np.clip(k_grp[1], -0.999, 0.999))
|
|
500
544
|
zdiff = (z1 - z2) / np.sqrt(1.0 / max(n1 - 3, 1) + 1.0 / max(n2 - 3, 1))
|
|
545
|
+
zdiff = np.where(no_target, np.nan, zdiff)
|
|
501
546
|
from scipy import stats as sps
|
|
502
|
-
out[f"kme_{levels[0]}"] = k_grp[0]
|
|
503
|
-
out[f"kme_{levels[1]}"] = k_grp[1]
|
|
547
|
+
out[f"kme_{levels[0]}"] = np.where(no_target, np.nan, k_grp[0])
|
|
548
|
+
out[f"kme_{levels[1]}"] = np.where(no_target, np.nan, k_grp[1])
|
|
504
549
|
out["z_interaction"] = zdiff
|
|
505
550
|
out["p_interaction"] = 2.0 * sps.norm.sf(np.abs(zdiff))
|
|
506
551
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|