embkit 0.3__tar.gz → 0.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. embkit-0.5/PKG-INFO +89 -0
  2. embkit-0.5/README.md +67 -0
  3. {embkit-0.3 → embkit-0.5}/pyproject.toml +2 -2
  4. {embkit-0.3 → embkit-0.5}/setup.py +1 -1
  5. {embkit-0.3 → embkit-0.5}/src/embkit/align.py +44 -16
  6. embkit-0.5/src/embkit/c_bio/api.py +31 -0
  7. {embkit-0.3 → embkit-0.5}/src/embkit/commands/align.py +3 -3
  8. embkit-0.5/src/embkit/commands/matrix.py +97 -0
  9. embkit-0.5/src/embkit/commands/model.py +434 -0
  10. {embkit-0.3 → embkit-0.5}/src/embkit/commands/protein.py +12 -8
  11. embkit-0.5/src/embkit/constraints/__init__.py +7 -0
  12. embkit-0.5/src/embkit/constraints/pathway_constraint.py +111 -0
  13. embkit-0.5/src/embkit/datasets/__init__.py +172 -0
  14. embkit-0.5/src/embkit/encoding/__init__.py +327 -0
  15. embkit-0.5/src/embkit/encoding/genome.py +74 -0
  16. {embkit-0.3 → embkit-0.5}/src/embkit/estimator/vae_estimator.py +10 -7
  17. embkit-0.5/src/embkit/factory/__init__.py +17 -0
  18. embkit-0.5/src/embkit/factory/core.py +153 -0
  19. {embkit-0.3 → embkit-0.5}/src/embkit/factory/layers.py +107 -122
  20. {embkit-0.3 → embkit-0.5}/src/embkit/factory/mapping.py +58 -0
  21. {embkit-0.3 → embkit-0.5}/src/embkit/factory/registry.py +31 -0
  22. embkit-0.5/src/embkit/files/h5.py +185 -0
  23. embkit-0.5/src/embkit/files/json.py +34 -0
  24. {embkit-0.3 → embkit-0.5}/src/embkit/files/read_csv.py +28 -9
  25. embkit-0.5/src/embkit/losses/__init__.py +23 -0
  26. embkit-0.5/src/embkit/losses/base.py +74 -0
  27. embkit-0.5/src/embkit/losses/vae_loss.py +242 -0
  28. embkit-0.5/src/embkit/models/ffnn.py +127 -0
  29. embkit-0.5/src/embkit/models/pair.py +70 -0
  30. {embkit-0.3 → embkit-0.5}/src/embkit/models/vae/__init__.py +3 -4
  31. embkit-0.5/src/embkit/models/vae/decoder.py +115 -0
  32. embkit-0.5/src/embkit/models/vae/encoder.py +384 -0
  33. embkit-0.5/src/embkit/models/vae/net_vae.py +422 -0
  34. embkit-0.5/src/embkit/models/vae/rna_vae.py +266 -0
  35. embkit-0.5/src/embkit/models/vae/vae.py +400 -0
  36. {embkit-0.3 → embkit-0.5}/src/embkit/modules/masked_linear.py +17 -2
  37. embkit-0.5/src/embkit/modules/mha.py +135 -0
  38. {embkit-0.3 → embkit-0.5}/src/embkit/modules/tsp.py +10 -6
  39. {embkit-0.3 → embkit-0.5}/src/embkit/optimize/__init__.py +102 -78
  40. embkit-0.5/src/embkit/optimize/multitask.py +276 -0
  41. embkit-0.5/src/embkit/pathway.py +228 -0
  42. embkit-0.5/src/embkit/preprocessing/normalize.py +107 -0
  43. {embkit-0.3 → embkit-0.5}/src/embkit/resources/c_bio_portal.py +9 -0
  44. {embkit-0.3 → embkit-0.5}/src/embkit/resources/gtex.py +21 -5
  45. {embkit-0.3 → embkit-0.5}/src/embkit/resources/resource.py +57 -10
  46. {embkit-0.3 → embkit-0.5}/src/embkit/utilities/pca.py +7 -4
  47. embkit-0.5/src/embkit.egg-info/PKG-INFO +89 -0
  48. {embkit-0.3 → embkit-0.5}/src/embkit.egg-info/SOURCES.txt +6 -2
  49. embkit-0.3/PKG-INFO +0 -71
  50. embkit-0.3/README.md +0 -49
  51. embkit-0.3/src/embkit/c_bio/api.py +0 -13
  52. embkit-0.3/src/embkit/commands/matrix.py +0 -36
  53. embkit-0.3/src/embkit/commands/model.py +0 -233
  54. embkit-0.3/src/embkit/constraints/__init__.py +0 -1
  55. embkit-0.3/src/embkit/constraints/network_constraint.py +0 -72
  56. embkit-0.3/src/embkit/datasets/__init__.py +0 -51
  57. embkit-0.3/src/embkit/encoding/__init__.py +0 -155
  58. embkit-0.3/src/embkit/factory/__init__.py +0 -17
  59. embkit-0.3/src/embkit/factory/core.py +0 -46
  60. embkit-0.3/src/embkit/files/h5.py +0 -131
  61. embkit-0.3/src/embkit/files/json.py +0 -20
  62. embkit-0.3/src/embkit/losses/__init__.py +0 -1
  63. embkit-0.3/src/embkit/losses/vae_loss.py +0 -109
  64. embkit-0.3/src/embkit/models/ffnn.py +0 -50
  65. embkit-0.3/src/embkit/models/vae/base_vae.py +0 -112
  66. embkit-0.3/src/embkit/models/vae/decoder.py +0 -84
  67. embkit-0.3/src/embkit/models/vae/encoder.py +0 -165
  68. embkit-0.3/src/embkit/models/vae/net_vae.py +0 -115
  69. embkit-0.3/src/embkit/models/vae/rna_vae.py +0 -282
  70. embkit-0.3/src/embkit/models/vae/vae.py +0 -102
  71. embkit-0.3/src/embkit/pathway.py +0 -227
  72. embkit-0.3/src/embkit/preprocessing/normalize.py +0 -72
  73. embkit-0.3/src/embkit.egg-info/PKG-INFO +0 -71
  74. {embkit-0.3 → embkit-0.5}/setup.cfg +0 -0
  75. {embkit-0.3 → embkit-0.5}/src/embkit/__init__.py +0 -0
  76. {embkit-0.3 → embkit-0.5}/src/embkit/__main__.py +0 -0
  77. {embkit-0.3 → embkit-0.5}/src/embkit/bmeg.py +0 -0
  78. {embkit-0.3 → embkit-0.5}/src/embkit/c_bio/__init__.py +0 -0
  79. {embkit-0.3 → embkit-0.5}/src/embkit/commands/__init__.py +0 -0
  80. {embkit-0.3 → embkit-0.5}/src/embkit/commands/cbio.py +0 -0
  81. {embkit-0.3 → embkit-0.5}/src/embkit/commands/resources.py +0 -0
  82. {embkit-0.3 → embkit-0.5}/src/embkit/encoding/protein.py +0 -0
  83. {embkit-0.3 → embkit-0.5}/src/embkit/estimator/__init__.py +0 -0
  84. {embkit-0.3 → embkit-0.5}/src/embkit/files/__init__.py +0 -0
  85. {embkit-0.3 → embkit-0.5}/src/embkit/files/loaders.py +0 -0
  86. {embkit-0.3 → embkit-0.5}/src/embkit/metrics/__init__.py +0 -0
  87. {embkit-0.3 → embkit-0.5}/src/embkit/metrics/distance/__init__.py +0 -0
  88. {embkit-0.3 → embkit-0.5}/src/embkit/metrics/distance/metrics.py +0 -0
  89. {embkit-0.3 → embkit-0.5}/src/embkit/models/__init__.py +0 -0
  90. {embkit-0.3 → embkit-0.5}/src/embkit/modules/__init__.py +0 -0
  91. {embkit-0.3 → embkit-0.5}/src/embkit/modules/pairwise_comparison_layer.py +0 -0
  92. {embkit-0.3 → embkit-0.5}/src/embkit/preprocessing/__init__.py +0 -0
  93. {embkit-0.3 → embkit-0.5}/src/embkit/preprocessing/dataset.py +0 -0
  94. {embkit-0.3 → embkit-0.5}/src/embkit/resources/__init__.py +0 -0
  95. {embkit-0.3 → embkit-0.5}/src/embkit/resources/hugo.py +0 -0
  96. {embkit-0.3 → embkit-0.5}/src/embkit/resources/sif.py +0 -0
  97. {embkit-0.3 → embkit-0.5}/src/embkit/resources/util.py +0 -0
  98. {embkit-0.3 → embkit-0.5}/src/embkit/utilities/__init__.py +0 -0
  99. {embkit-0.3 → embkit-0.5}/src/embkit/utilities/kmeans.py +0 -0
  100. {embkit-0.3 → embkit-0.5}/src/embkit/version.py +0 -0
  101. {embkit-0.3 → embkit-0.5}/src/embkit.egg-info/dependency_links.txt +0 -0
  102. {embkit-0.3 → embkit-0.5}/src/embkit.egg-info/entry_points.txt +0 -0
  103. {embkit-0.3 → embkit-0.5}/src/embkit.egg-info/requires.txt +0 -0
  104. {embkit-0.3 → embkit-0.5}/src/embkit.egg-info/top_level.txt +0 -0
  105. {embkit-0.3 → embkit-0.5}/tests/test_main.py +0 -0
embkit-0.5/PKG-INFO ADDED
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: embkit
3
+ Version: 0.5
4
+ Summary: Embedding Kit for embedding models
5
+ Author-email: Kyle Ellrott <ellrott@ohsu.edu>, Raphael Kirchgaessner <kirchgae@ohsu.edu>
6
+ License-Expression: MIT
7
+ Keywords: embeddings,machine learning,deep learning
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: torch>=2.0
11
+ Requires-Dist: pandas
12
+ Requires-Dist: numpy
13
+ Requires-Dist: click
14
+ Requires-Dist: scikit-learn
15
+ Requires-Dist: scipy
16
+ Requires-Dist: gripql
17
+ Requires-Dist: tqdm
18
+ Requires-Dist: biopython
19
+ Requires-Dist: fair-esm
20
+ Requires-Dist: faiss-cpu
21
+ Requires-Dist: h5py
22
+
23
+
24
+ # Embedding Kit
25
+
26
+ Methods for data normalization, embedding, synthesis and transformation in computational biology workflows.
27
+
28
+ Embedding Kit (embkit) is a toolkit for building and applying embedding models. It combines:
29
+
30
+ - CLI commands for repeatable model training and encoding
31
+ - PyTorch VAE model components for custom pipelines
32
+ - Utilities for loading, normalizing, and aligning large molecular datasets
33
+ - ESM2-based protein sequence embeddings
34
+
35
+ Use Embedding Kit when you want to move from tabular molecular data (RNA-seq, proteomics, methylation) to trainable latent representations that can be reused for downstream analysis.
36
+
37
+ ## Features
38
+
39
+ - **VAE & NetVAE training** — Train variational autoencoders, including pathway-constrained NetVAE models, from tabular or HDF5 matrices.
40
+ - **Beta-KL scheduling** — Schedule the KL-regularization weight across training epochs for stable convergence.
41
+ - **Protein embeddings** — Generate sequence embeddings from FASTA files using ESM2 models.
42
+ - **Normalization utilities** — Min-max and exponential min-max normalization for expression matrices.
43
+ - **Device auto-detection** — Runs on CPU, CUDA, or Apple Metal GPUs.
44
+ - **Python API** — Composable layers, losses, and model factories for custom pipelines beyond the CLI.
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install embkit
50
+ ```
51
+
52
+
53
+ ## Training in Python
54
+
55
+ ```python
56
+ from embkit import dataframe_loader
57
+ from embkit.models.vae import VAE
58
+ from embkit.factory.layers import Layer
59
+ from embkit.losses import BCEWithLogitsVAELoss
60
+ from embkit.factory import save, load
61
+ from embkit import optimize
62
+
63
+ loader = dataframe_loader(df_norm, batch_size=256)
64
+
65
+ vae = VAE(
66
+ features=list(df_norm.columns),
67
+ latent_dim=128,
68
+ encoder_layers=[Layer(512, activation="relu"), Layer(256, activation="relu")],
69
+ decoder_layers=[Layer(512, activation="relu")],
70
+ )
71
+
72
+ optimize.fit_vae(vae, X=loader, epochs=60, lr=1e-3, loss=BCEWithLogitsVAELoss())
73
+
74
+ save(vae, "vae.model")
75
+ ```
76
+
77
+ ## Documentation
78
+
79
+ Full documentation, including core concepts, the training guide, the CLI reference, and API docs, is available in the [docs folder](docs/). To build it locally, see [DEV.md](DEV.md).
80
+
81
+ ## License
82
+
83
+ MIT
84
+
85
+ ## Authors
86
+
87
+ - Kyle Ellrott — ellrott@ohsu.edu
88
+ - Raphael Kirchgaessner — kirchgae@ohsu.edu
89
+
embkit-0.5/README.md ADDED
@@ -0,0 +1,67 @@
1
+
2
+ # Embedding Kit
3
+
4
+ Methods for data normalization, embedding, synthesis and transformation in computational biology workflows.
5
+
6
+ Embedding Kit (embkit) is a toolkit for building and applying embedding models. It combines:
7
+
8
+ - CLI commands for repeatable model training and encoding
9
+ - PyTorch VAE model components for custom pipelines
10
+ - Utilities for loading, normalizing, and aligning large molecular datasets
11
+ - ESM2-based protein sequence embeddings
12
+
13
+ Use Embedding Kit when you want to move from tabular molecular data (RNA-seq, proteomics, methylation) to trainable latent representations that can be reused for downstream analysis.
14
+
15
+ ## Features
16
+
17
+ - **VAE & NetVAE training** — Train variational autoencoders, including pathway-constrained NetVAE models, from tabular or HDF5 matrices.
18
+ - **Beta-KL scheduling** — Schedule the KL-regularization weight across training epochs for stable convergence.
19
+ - **Protein embeddings** — Generate sequence embeddings from FASTA files using ESM2 models.
20
+ - **Normalization utilities** — Min-max and exponential min-max normalization for expression matrices.
21
+ - **Device auto-detection** — Runs on CPU, CUDA, or Apple Metal GPUs.
22
+ - **Python API** — Composable layers, losses, and model factories for custom pipelines beyond the CLI.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install embkit
28
+ ```
29
+
30
+
31
+ ## Training in Python
32
+
33
+ ```python
34
+ from embkit import dataframe_loader
35
+ from embkit.models.vae import VAE
36
+ from embkit.factory.layers import Layer
37
+ from embkit.losses import BCEWithLogitsVAELoss
38
+ from embkit.factory import save, load
39
+ from embkit import optimize
40
+
41
+ loader = dataframe_loader(df_norm, batch_size=256)
42
+
43
+ vae = VAE(
44
+ features=list(df_norm.columns),
45
+ latent_dim=128,
46
+ encoder_layers=[Layer(512, activation="relu"), Layer(256, activation="relu")],
47
+ decoder_layers=[Layer(512, activation="relu")],
48
+ )
49
+
50
+ optimize.fit_vae(vae, X=loader, epochs=60, lr=1e-3, loss=BCEWithLogitsVAELoss())
51
+
52
+ save(vae, "vae.model")
53
+ ```
54
+
55
+ ## Documentation
56
+
57
+ Full documentation, including core concepts, the training guide, the CLI reference, and API docs, is available in the [docs folder](docs/). To build it locally, see [DEV.md](DEV.md).
58
+
59
+ ## License
60
+
61
+ MIT
62
+
63
+ ## Authors
64
+
65
+ - Kyle Ellrott — ellrott@ohsu.edu
66
+ - Raphael Kirchgaessner — kirchgae@ohsu.edu
67
+
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "embkit"
7
- version = "0.3"
7
+ version = "0.5"
8
8
  description = "Embedding Kit for embedding models"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
11
- license = { text = "MIT" }
11
+ license = "MIT"
12
12
  authors = [
13
13
  { name = "Kyle Ellrott", email = "ellrott@ohsu.edu" },
14
14
  { name = "Raphael Kirchgaessner", email = "kirchgae@ohsu.edu" }
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='embedding-kit',
5
- version='0.1',
5
+ version='0.5',
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  # List your dependencies here!
@@ -58,8 +58,7 @@ def matrix_spearman_alignment_linear(a, b, cutoff=0.0):
58
58
 
59
59
  def procrustes(X, Y):
60
60
  """
61
- Computes the Procrustes transformation (optimal PURE rotation)
62
- to align X to Y, correcting for reflections.
61
+ Computes the Procrustes transformation (optimal PURE rotation) to align X to Y, correcting for reflections.
63
62
 
64
63
  Args:
65
64
  X: The first matrix (N_points, N_dims).
@@ -67,6 +66,7 @@ def procrustes(X, Y):
67
66
 
68
67
  Returns:
69
68
  R: The optimal rotation matrix (guaranteed det(R) = +1).
69
+ S: Singular values of X.T @ Y
70
70
  """
71
71
  assert X.shape == Y.shape
72
72
 
@@ -74,37 +74,65 @@ def procrustes(X, Y):
74
74
 
75
75
  U, S, Vt = np.linalg.svd(M)
76
76
 
77
- R = np.dot(U, Vt)
77
+ R = np.dot(U, Vt) # R can be optimized indep. of scalar
78
78
 
79
79
  if np.linalg.det(R) < 0:
80
80
  Vt_corrected = Vt.copy()
81
81
  Vt_corrected[-1, :] *= -1
82
82
  R = np.dot(U, Vt_corrected)
83
83
 
84
- return R
85
-
84
+ return R,S
86
85
 
87
86
  def procrustes_scale(X, Y):
88
87
  """
89
- Compute the procrustes transformation (R), then compute a factor (k) to rescale the src matrix.
88
+ Compute the procrustes transformation (R), then a single (scalar) scale factor k = ||Y||_F / ||X||_F (equivalently ||Y||_F / ||X @ R||_F, since rotation preserves Frobenius norm).
89
+ Note: Scaling is calculated without Least-Squares, unlike more traditional approaches, to match Y's spread directly and avoid shrinking under noisy correspondances.
90
90
 
91
- To apply transformtion:
91
+ To apply transformation:
92
92
  src.dot(R) * k
93
93
 
94
-
95
94
  Args:
96
95
  X: The first matrix (N_points, N_dims).
97
96
  Y: The second matrix (N_points, N_dims).
98
97
 
99
98
  Returns:
100
99
  R: The optimal rotation matrix (guaranteed det(R) = +1).
101
- k: Scaling factor
100
+ k: Scalar scaling factor, ||Y||_F / ||X||_F
101
+ S: Singular values of X.T @ Y
102
102
 
103
103
  """
104
- R = procrustes(X,Y)
105
- A = np.array(X.dot(R))
106
- B = np.array(Y)
107
- numerators = np.sum(A * B, axis=0)
108
- denominators = np.sum(A * A, axis=0)
109
- k = np.divide(numerators, denominators, out=np.zeros_like(numerators), where=denominators!=0)
110
- return R, k
104
+ R, S = procrustes(X, Y)
105
+ Xnorm = np.linalg.norm(X)
106
+ k = np.linalg.norm(Y) / Xnorm if Xnorm != 0 else 0.0 # Unlike Least Squares (superceded), we dont bias towards shrinking under noisy correspondence.
107
+ return R, k, S
108
+
109
+ def procrustes_scale_centered(X, Y):
110
+ """
111
+ Mean-centers X and Y, fits the rotation, then scales: k = ||Yc||_F / ||Xc||_F (equivalently ||Yc||_F / ||Xc @ R||_F, since rotation preserves Frobenius norm).
112
+ Note: Scaling is calculated without Least-Squares, unlike more traditional approaches, to match Y's spread directly and avoid shrinking under noisy correspondances.
113
+
114
+ To apply transformation:
115
+ (src - Xmean).dot(R) * k + Ymean
116
+
117
+ Args:
118
+ X: The first matrix (N_points, N_dims).
119
+ Y: The second matrix (N_points, N_dims).
120
+
121
+ Returns:
122
+ R: The optimal rotation matrix (guaranteed det(R) = +1).
123
+ k: Scalar scaling factor, ||Yc||_F / ||Xc||_F
124
+ Xmean: Per-dimension mean of X (shape: (N_dims,)), subtract before applying R/k
125
+ Ymean: Per-dimension mean of Y (shape: (N_dims,)), add back after applying R/k
126
+ S: Singular values of (X - Xmean).T @ (Y - Ymean)
127
+
128
+ """
129
+ Xmean = np.array(X).mean(axis=0)
130
+ Ymean = np.array(Y).mean(axis=0)
131
+ Xc = np.array(X) - Xmean
132
+ Yc = np.array(Y) - Ymean
133
+ R, k, S = procrustes_scale(Xc, Yc)
134
+ return R, k, Xmean, Ymean, S
135
+
136
+
137
+
138
+
@@ -0,0 +1,31 @@
1
+ import requests
2
+ import logging
3
+
4
+ logger = logging.getLogger(__name__)
5
+
6
+
7
+ class CBIOAPI:
8
+ """
9
+ Small client for the cBioPortal public API.
10
+
11
+ cBioPortal is a public catalog of cancer genomics studies. This helper
12
+ wraps the lightweight metadata endpoints (like listing available studies)
13
+ that don't require downloading study data — use the
14
+ :class:`~embkit.resources.c_bio_portal.CBIOPortal` resource for fetching an
15
+ actual study's molecular profile data.
16
+ """
17
+
18
+ @staticmethod
19
+ def list_studies():
20
+ """
21
+ List the cBioPortal studies available for download.
22
+
23
+ Returns:
24
+ The API's JSON list of studies, or ``None`` if the request failed.
25
+ """
26
+ try:
27
+ studies = requests.get("https://www.cbioportal.org/api/studies").json()
28
+ return studies
29
+ except requests.RequestException as e:
30
+ logger.error("Error fetching studies: %s", e)
31
+ return None
@@ -25,6 +25,6 @@ def pair(matrix1, matrix2, method, cutoff):
25
25
  m2 = pd.read_csv(matrix2, sep="\t", index_col=0)
26
26
 
27
27
  if method == "linear":
28
- out = matrix_spearman_alignment_linear(m1, m2, cutoff)
29
- for k, v in out.items():
30
- print(f"{k}\t{v[0]}\t{v[1]}")
28
+ out_a, out_b, out_score = matrix_spearman_alignment_linear(m1, m2, cutoff)
29
+ for a_id, b_id, score in zip(out_a, out_b, out_score):
30
+ click.echo(f"{a_id}\t{b_id}\t{score}")
@@ -0,0 +1,97 @@
1
+ """
2
+ embkit matrix — preprocessing of expression/count matrices.
3
+
4
+ The ``matrix`` command group prepares the tab-separated matrices that become the
5
+ input to an embedding model. Its subcommands implement the "normalize" and "pca"
6
+ major operations: rescaling raw feature values to a comparable ``[0, 1]`` range,
7
+ and projecting a matrix onto its leading principal components. These are the
8
+ data-preparation steps you run before (and independently of) training.
9
+ """
10
+ import click
11
+ import pandas as pd
12
+ from pathlib import Path
13
+ from ..utilities.pca import run_pca
14
+
15
+
16
+ matrix = click.Group(name="matrix", help="Model commands.")
17
+
18
+ @matrix.command()
19
+ @click.argument("srcs", nargs=-1)
20
+ @click.option("--out", "-o", required=True)
21
+ @click.option("--features", default=None, type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str))
22
+ @click.option("--col-quantile", "col_quantile", default=False, is_flag=True)
23
+ @click.option("--quantile-max", "quantile_max", default=0.9, type=float)
24
+ @click.option("--precision", "-p", type=int, default=5)
25
+ def normalize(srcs, out, features, col_quantile, quantile_max, precision):
26
+ """
27
+ embkit matrix normalize — rescale one or more matrices for embedding training.
28
+
29
+ Reads tab-separated expression/count matrices, optionally subsets them to a
30
+ list of features, concatenates them, and writes a single normalized matrix in
31
+ which each sample's values fall in ``[0, 1]``. This is the preprocessing step
32
+ that puts the input on a comparable scale before a VAE / NetVAE is trained.
33
+
34
+ By default each *sample* (row) is divided by its own ``quantile_max``
35
+ quantile; passing ``--col-quantile`` divides each *feature* (column) by its
36
+ quantile instead.
37
+
38
+ Args:
39
+ srcs: One or more input TSV matrices (index column required).
40
+ out: Path to write the normalized TSV.
41
+ features: Optional file listing feature names to keep.
42
+ col_quantile: Normalize per feature (column) rather than per sample.
43
+ quantile_max: The quantile used as the divisor (default 0.9).
44
+ precision: Number of decimals to round to in the output.
45
+ """
46
+
47
+ features_list = None
48
+ if features is not None:
49
+ with open(features, encoding="ascii") as handle:
50
+ features_list = list(line.rstrip() for line in handle)
51
+
52
+ dfs = []
53
+ for i in srcs:
54
+ df = pd.read_csv(i, sep="\t", index_col=0)
55
+ if features_list is not None:
56
+ df = df[features_list]
57
+ dfs.append(df)
58
+ if len(dfs) == 0:
59
+ click.echo("No matrices defined")
60
+ return
61
+ df = pd.concat( dfs )
62
+
63
+ if not col_quantile:
64
+ normDF = (df.transpose() / df.quantile(quantile_max, axis=1)).transpose().clip(upper=1.0, lower=0.0).fillna(0.0)
65
+ else:
66
+ normDF = (df / df.quantile(quantile_max)).clip(upper=1.0, lower=0.0).fillna(0.0)
67
+ normDF.round(decimals=precision).to_csv(out, sep="\t")
68
+
69
+ @matrix.command()
70
+ @click.argument("input_path", type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str))
71
+ @click.option("--pca-size", required=True, type=int, help="Number of principal components.")
72
+ @click.option("--out", "-o", default=None, type=str, help="Output TSV path.")
73
+ def pca(input_path, pca_size, out):
74
+ """
75
+ embkit matrix pca — project a matrix onto its leading principal components.
76
+
77
+ Runs principal component analysis on a tab-separated matrix and writes the
78
+ first ``pca_size`` components to a TSV. Useful for reducing dimensionality
79
+ before downstream analysis or visualization (e.g. inspecting whether a
80
+ normalized matrix separates samples as expected).
81
+
82
+ If no ``--out`` is given, the output defaults to ``<input_stem>.pca.tsv``.
83
+
84
+ Args:
85
+ input_path: Input TSV matrix.
86
+ pca_size: Number of principal components to retain (must be > 0).
87
+ out: Output TSV path (optional; defaults to ``<input_stem>.pca.tsv``).
88
+ """
89
+ if pca_size <= 0:
90
+ raise click.BadParameter("--pca-size must be a positive integer.")
91
+
92
+ if out is None:
93
+ out = f"{Path(input_path).stem}.pca.tsv"
94
+ click.echo(f"No output path provided, using default naming: {out}")
95
+
96
+ run_pca(input_file=input_path, pca_size=pca_size, output_file=out)
97
+ click.echo(f"PCA saved to {out}")