embkit 0.1.0__tar.gz → 0.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.
- embkit-0.2/PKG-INFO +69 -0
- embkit-0.2/README.md +49 -0
- {embkit-0.1.0 → embkit-0.2}/pyproject.toml +11 -3
- embkit-0.2/src/embkit/__init__.py +62 -0
- embkit-0.2/src/embkit/__main__.py +81 -0
- embkit-0.2/src/embkit/align.py +151 -0
- {embkit-0.1.0 → embkit-0.2}/src/embkit/bmeg.py +7 -0
- embkit-0.2/src/embkit/c_bio/__init__.py +1 -0
- embkit-0.2/src/embkit/c_bio/api.py +13 -0
- embkit-0.2/src/embkit/commands/__init__.py +20 -0
- embkit-0.2/src/embkit/commands/align.py +35 -0
- embkit-0.2/src/embkit/commands/cbio.py +63 -0
- embkit-0.2/src/embkit/commands/datasets.py +61 -0
- embkit-0.2/src/embkit/commands/matrix.py +36 -0
- embkit-0.2/src/embkit/commands/model.py +220 -0
- embkit-0.2/src/embkit/commands/protein.py +56 -0
- embkit-0.2/src/embkit/datasets/__init__.py +4 -0
- embkit-0.2/src/embkit/datasets/c_bio_portal.py +134 -0
- embkit-0.2/src/embkit/datasets/dataset.py +138 -0
- embkit-0.2/src/embkit/datasets/gtex.py +109 -0
- embkit-0.2/src/embkit/datasets/hugo.py +17 -0
- embkit-0.2/src/embkit/datasets/sif.py +17 -0
- embkit-0.2/src/embkit/datasets/util.py +49 -0
- {embkit-0.1.0 → embkit-0.2}/src/embkit/estimator/vae_estimator.py +14 -3
- embkit-0.2/src/embkit/file_readers/__init__.py +1 -0
- embkit-0.2/src/embkit/file_readers/read_csv.py +252 -0
- embkit-0.2/src/embkit/layers/__init__.py +33 -0
- embkit-0.2/src/embkit/layers/constraint_info.py +111 -0
- embkit-0.2/src/embkit/layers/layer_info.py +108 -0
- embkit-0.2/src/embkit/layers/masked_linear.py +88 -0
- embkit-0.2/src/embkit/layers/pairwise_comparison_layer.py +91 -0
- embkit-0.2/src/embkit/layers/tsp.py +103 -0
- embkit-0.2/src/embkit/losses/__init__.py +1 -0
- embkit-0.2/src/embkit/losses/vae_loss.py +108 -0
- embkit-0.2/src/embkit/metrics/distance/__init__.py +1 -0
- embkit-0.2/src/embkit/metrics/distance/metrics.py +36 -0
- embkit-0.2/src/embkit/models/__init__.py +21 -0
- embkit-0.2/src/embkit/models/ffnn.py +151 -0
- embkit-0.2/src/embkit/models/hla2vec.py +307 -0
- embkit-0.2/src/embkit/models/protein.py +175 -0
- embkit-0.2/src/embkit/models/vae/__init__.py +10 -0
- embkit-0.2/src/embkit/models/vae/base_vae.py +327 -0
- embkit-0.2/src/embkit/models/vae/decoder.py +58 -0
- embkit-0.2/src/embkit/models/vae/encoder.py +145 -0
- embkit-0.2/src/embkit/models/vae/hla2vec.py +307 -0
- embkit-0.2/src/embkit/models/vae/net_vae.py +192 -0
- embkit-0.2/src/embkit/models/vae/pytorch.py +150 -0
- embkit-0.2/src/embkit/models/vae/rna_vae.py +282 -0
- embkit-0.2/src/embkit/models/vae/vae.py +218 -0
- embkit-0.2/src/embkit/pathway.py +177 -0
- embkit-0.2/src/embkit/preprocessing/__init__.py +8 -0
- embkit-0.2/src/embkit/preprocessing/loaders.py +40 -0
- embkit-0.2/src/embkit/utilities/__init__.py +2 -0
- embkit-0.2/src/embkit/utilities/kmeans.py +46 -0
- embkit-0.2/src/embkit/utilities/pca.py +38 -0
- embkit-0.2/src/embkit.egg-info/PKG-INFO +69 -0
- embkit-0.2/src/embkit.egg-info/SOURCES.txt +67 -0
- embkit-0.2/src/embkit.egg-info/entry_points.txt +2 -0
- {embkit-0.1.0 → embkit-0.2}/src/embkit.egg-info/requires.txt +5 -0
- embkit-0.2/tests/test_main.py +65 -0
- embkit-0.1.0/PKG-INFO +0 -42
- embkit-0.1.0/README.md +0 -27
- embkit-0.1.0/src/embkit/__main__.py +0 -14
- embkit-0.1.0/src/embkit/align.py +0 -59
- embkit-0.1.0/src/embkit/commands.py +0 -27
- embkit-0.1.0/src/embkit/layers/__init__.py +0 -1
- embkit-0.1.0/src/embkit/layers/masked_linear.py +0 -28
- embkit-0.1.0/src/embkit/losses/__init__.py +0 -1
- embkit-0.1.0/src/embkit/losses/vae_loss.py +0 -16
- embkit-0.1.0/src/embkit/models/__init__.py +0 -1
- embkit-0.1.0/src/embkit/models/vae_model/__init__.py +0 -3
- embkit-0.1.0/src/embkit/models/vae_model/base_vae.py +0 -226
- embkit-0.1.0/src/embkit/models/vae_model/decoder.py +0 -17
- embkit-0.1.0/src/embkit/models/vae_model/encoder.py +0 -99
- embkit-0.1.0/src/embkit/models/vae_model/net_vae.py +0 -186
- embkit-0.1.0/src/embkit/pathway.py +0 -8
- embkit-0.1.0/src/embkit/vae.py +0 -97
- embkit-0.1.0/src/embkit.egg-info/PKG-INFO +0 -42
- embkit-0.1.0/src/embkit.egg-info/SOURCES.txt +0 -30
- {embkit-0.1.0 → embkit-0.2}/setup.cfg +0 -0
- {embkit-0.1.0 → embkit-0.2}/setup.py +0 -0
- {embkit-0.1.0 → embkit-0.2}/src/embkit/constraints/__init__.py +0 -0
- {embkit-0.1.0 → embkit-0.2}/src/embkit/constraints/network_constraint.py +0 -0
- {embkit-0.1.0/src/embkit → embkit-0.2/src/embkit/estimator}/__init__.py +0 -0
- {embkit-0.1.0/src/embkit/estimator → embkit-0.2/src/embkit/metrics}/__init__.py +0 -0
- {embkit-0.1.0/src/embkit → embkit-0.2/src/embkit/preprocessing}/normalize.py +0 -0
- {embkit-0.1.0 → embkit-0.2}/src/embkit.egg-info/dependency_links.txt +0 -0
- {embkit-0.1.0 → embkit-0.2}/src/embkit.egg-info/top_level.txt +0 -0
embkit-0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: embkit
|
|
3
|
+
Version: 0.2
|
|
4
|
+
Summary: Embedding Kit for embedding models
|
|
5
|
+
Author-email: Kyle Ellrott <ellrott@ohsu.edu>, Raphael Kirchgaessner <kirchgae@ohsu.edu>
|
|
6
|
+
License: 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
|
+
|
|
21
|
+
|
|
22
|
+
# Embedding Kit
|
|
23
|
+
|
|
24
|
+
Methods for data normalization, embedding, synthesis and transformation
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
```
|
|
29
|
+
pip install embkit
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
# Training a model
|
|
33
|
+
```
|
|
34
|
+
embkit model train-vae ./experiments/tcga/tumor.normalized.tsv --epochs 120
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Development
|
|
38
|
+
|
|
39
|
+
To install the library locally use:
|
|
40
|
+
```pip install -e .```
|
|
41
|
+
```python setup.py build```
|
|
42
|
+
```python setup.py install```
|
|
43
|
+
|
|
44
|
+
### To run tests use:
|
|
45
|
+
```bash
|
|
46
|
+
coverage run --source=embkit -m unittest discover -s tests
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
To generate a coverage report use:
|
|
50
|
+
```bash
|
|
51
|
+
coverage html
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To open the coverage report in a browser, run:
|
|
55
|
+
#### MacOS:
|
|
56
|
+
```bash
|
|
57
|
+
open htmlcov/index.html
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Linux:
|
|
61
|
+
```bash
|
|
62
|
+
xdg-open htmlcov/index.html
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Windows:
|
|
66
|
+
```bash
|
|
67
|
+
start htmlcov\index.html
|
|
68
|
+
```
|
|
69
|
+
|
embkit-0.2/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
# Embedding Kit
|
|
3
|
+
|
|
4
|
+
Methods for data normalization, embedding, synthesis and transformation
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
```
|
|
9
|
+
pip install embkit
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
# Training a model
|
|
13
|
+
```
|
|
14
|
+
embkit model train-vae ./experiments/tcga/tumor.normalized.tsv --epochs 120
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Development
|
|
18
|
+
|
|
19
|
+
To install the library locally use:
|
|
20
|
+
```pip install -e .```
|
|
21
|
+
```python setup.py build```
|
|
22
|
+
```python setup.py install```
|
|
23
|
+
|
|
24
|
+
### To run tests use:
|
|
25
|
+
```bash
|
|
26
|
+
coverage run --source=embkit -m unittest discover -s tests
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To generate a coverage report use:
|
|
30
|
+
```bash
|
|
31
|
+
coverage html
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
To open the coverage report in a browser, run:
|
|
35
|
+
#### MacOS:
|
|
36
|
+
```bash
|
|
37
|
+
open htmlcov/index.html
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### Linux:
|
|
41
|
+
```bash
|
|
42
|
+
xdg-open htmlcov/index.html
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### Windows:
|
|
46
|
+
```bash
|
|
47
|
+
start htmlcov\index.html
|
|
48
|
+
```
|
|
49
|
+
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "embkit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2"
|
|
8
8
|
description = "Embedding Kit for embedding models"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -19,11 +19,19 @@ dependencies = [
|
|
|
19
19
|
"pandas",
|
|
20
20
|
"numpy",
|
|
21
21
|
"click",
|
|
22
|
-
"scikit-learn"
|
|
22
|
+
"scikit-learn",
|
|
23
|
+
"scipy",
|
|
24
|
+
"gripql",
|
|
25
|
+
"tqdm",
|
|
26
|
+
"biopython",
|
|
27
|
+
"fair-esm"
|
|
23
28
|
]
|
|
24
29
|
|
|
25
30
|
[tool.setuptools]
|
|
26
31
|
package-dir = { "" = "src" }
|
|
27
32
|
|
|
28
33
|
[tool.setuptools.packages.find]
|
|
29
|
-
where = ["src"]
|
|
34
|
+
where = ["src"]
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
embkit = "embkit.__main__:cli"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Embedding Kit
|
|
3
|
+
|
|
4
|
+
The base module contains frequently used methods and functions
|
|
5
|
+
"""
|
|
6
|
+
import logging
|
|
7
|
+
import torch
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import numpy as np
|
|
10
|
+
from torch.utils.data import TensorDataset, DataLoader
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
def get_device() -> torch.device:
|
|
15
|
+
"""
|
|
16
|
+
get_device - scan system and return default device
|
|
17
|
+
"""
|
|
18
|
+
if torch.cuda.is_available():
|
|
19
|
+
logger.info("Using CUDA GPU")
|
|
20
|
+
return torch.device("cuda")
|
|
21
|
+
if torch.backends.mps.is_available():
|
|
22
|
+
logger.info("Using Metal GPU")
|
|
23
|
+
return torch.device("mps")
|
|
24
|
+
logger.info("Using CPU")
|
|
25
|
+
return torch.device("cpu")
|
|
26
|
+
|
|
27
|
+
def dataframe_tensor(df: pd.DataFrame,device=None) -> torch.Tensor:
|
|
28
|
+
"""
|
|
29
|
+
dataframe_tensor
|
|
30
|
+
|
|
31
|
+
take a pandas dataframe, create a tensor
|
|
32
|
+
"""
|
|
33
|
+
if device is None:
|
|
34
|
+
device = get_device()
|
|
35
|
+
return torch.from_numpy(df.values.astype(np.float32)).to(device)
|
|
36
|
+
|
|
37
|
+
def tensor_dataframe(tn: torch.Tensor, index=None, columns=None) -> pd.DataFrame:
|
|
38
|
+
"""
|
|
39
|
+
dataframe_tensor
|
|
40
|
+
|
|
41
|
+
take a torch tensor, create a pandas dataframe
|
|
42
|
+
"""
|
|
43
|
+
return pd.DataFrame(tn.cpu().detach().numpy(), index=index, columns=columns)
|
|
44
|
+
|
|
45
|
+
def dataframe_loader(df: pd.DataFrame,
|
|
46
|
+
batch_size = 256, shuffle=True,
|
|
47
|
+
device=None) -> torch.utils.data.DataLoader:
|
|
48
|
+
"""
|
|
49
|
+
dataframe_loader
|
|
50
|
+
|
|
51
|
+
:param df: Pandas Dataframe to be converted into passed to DataLoader
|
|
52
|
+
:param batch_size: value to be passed to DataLoader batch_size
|
|
53
|
+
:param shuffle: value to be passed to DataLoader shuffle
|
|
54
|
+
:param device: Device the where tensor should be moved. If None, use get_device
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
if device is None:
|
|
58
|
+
device = get_device()
|
|
59
|
+
x = torch.from_numpy(df.values.astype(np.float32)).to(device)
|
|
60
|
+
dataset = TensorDataset(x)
|
|
61
|
+
data_loader = DataLoader(dataset, batch_size=batch_size, shuffle=shuffle)
|
|
62
|
+
return data_loader
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from .commands import model, matrix, cbio_cmd, protein, datasets, align
|
|
5
|
+
|
|
6
|
+
@click.group(invoke_without_command=True)
|
|
7
|
+
@click.option('--verbose', is_flag=True, help="Enable verbose logging")
|
|
8
|
+
@click.pass_context
|
|
9
|
+
def cli(ctx, verbose):
|
|
10
|
+
"""Embedding Kit CLI.
|
|
11
|
+
|
|
12
|
+
Use 'embkit help [COMMAND]' for details on a specific command.
|
|
13
|
+
"""
|
|
14
|
+
# Set up logging based on the verbose flag
|
|
15
|
+
logging.basicConfig(
|
|
16
|
+
level=logging.DEBUG if verbose else logging.INFO,
|
|
17
|
+
format="%(asctime)s [%(levelname)s] %(message)s"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
ctx.ensure_object(dict)
|
|
21
|
+
ctx.obj["VERBOSE"] = verbose
|
|
22
|
+
|
|
23
|
+
if ctx.invoked_subcommand is None:
|
|
24
|
+
click.echo(ctx.get_help())
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Register our commands here
|
|
28
|
+
cli.add_command(model)
|
|
29
|
+
cli.add_command(matrix)
|
|
30
|
+
cli.add_command(cbio_cmd)
|
|
31
|
+
cli.add_command(protein)
|
|
32
|
+
cli.add_command(datasets)
|
|
33
|
+
cli.add_command(align)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ---- 'help' command ----
|
|
37
|
+
@cli.command("help", context_settings=dict(ignore_unknown_options=True))
|
|
38
|
+
@click.argument("path", nargs=-1)
|
|
39
|
+
@click.pass_context
|
|
40
|
+
def help_cmd(ctx, path):
|
|
41
|
+
"""
|
|
42
|
+
Show help for the CLI or a specific command.
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
embkit help
|
|
46
|
+
embkit help model
|
|
47
|
+
embkit help model train
|
|
48
|
+
"""
|
|
49
|
+
# No args -> show top-level help plus a neat command list summary
|
|
50
|
+
if not path:
|
|
51
|
+
click.echo(ctx.parent.get_help())
|
|
52
|
+
click.echo("\nCommands:")
|
|
53
|
+
# Sorted, with 1-line summaries
|
|
54
|
+
for name in sorted(cli.commands):
|
|
55
|
+
cmd = cli.commands[name]
|
|
56
|
+
summary = (cmd.help or cmd.short_help or "").strip().splitlines()[0]
|
|
57
|
+
click.echo(f" {name:15s} {summary}")
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
# Resolve dotted/space-separated path to a nested command
|
|
61
|
+
cmd = cli
|
|
62
|
+
info_name = []
|
|
63
|
+
parent = ctx.parent # start from top-level context
|
|
64
|
+
for part in path:
|
|
65
|
+
if not hasattr(cmd, "get_command"):
|
|
66
|
+
click.echo(cli.get_help(ctx)) # pragma: no cover
|
|
67
|
+
nxt_cmd = cmd.get_command(parent, part)
|
|
68
|
+
if nxt_cmd is None:
|
|
69
|
+
raise click.UsageError(f"Unknown command: {' '.join(path)}")
|
|
70
|
+
info_name.append(part)
|
|
71
|
+
cmd = nxt_cmd
|
|
72
|
+
parent = click.Context(cmd, info_name=part, parent=parent)
|
|
73
|
+
|
|
74
|
+
# Print help for the resolved command
|
|
75
|
+
click.echo(cmd.get_help(parent))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
cli_main = cli
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__": # pragma: no cover
|
|
81
|
+
cli()
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from scipy.stats import spearmanr
|
|
4
|
+
from scipy.optimize import linear_sum_assignment
|
|
5
|
+
from hopcroftkarp import HopcroftKarp
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def calc_rmsd(array1, array2):
|
|
9
|
+
"""
|
|
10
|
+
Calculates the Root Mean Square Deviation (RMSD) between two arrays.
|
|
11
|
+
Parameters:
|
|
12
|
+
array1 (numpy.ndarray): The first array.
|
|
13
|
+
array2 (numpy.ndarray): The second array, must be of the same length as array1.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
float: The RMSD between the two arrays.
|
|
17
|
+
"""
|
|
18
|
+
if len(array1) != len(array2):
|
|
19
|
+
raise ValueError("Arrays must be of the same length")
|
|
20
|
+
diff = array1 - array2
|
|
21
|
+
squared_diff = diff ** 2
|
|
22
|
+
mean_squared_diff = np.mean(squared_diff)
|
|
23
|
+
rmsd = np.sqrt(mean_squared_diff)
|
|
24
|
+
|
|
25
|
+
return rmsd
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def matrix_spearman_alignment_linear(a, b, cutoff=0.0):
|
|
29
|
+
"""
|
|
30
|
+
matrix_spearman_alignment_linear
|
|
31
|
+
|
|
32
|
+
Uses the linear_sum_assignment algorithm to create an optimal
|
|
33
|
+
mapping between two matrices by maximizing the total Spearman
|
|
34
|
+
correlation score.
|
|
35
|
+
"""
|
|
36
|
+
# Identify overlapping columns
|
|
37
|
+
isect = a.columns.intersection(b.columns)
|
|
38
|
+
|
|
39
|
+
combined_df = pd.concat([a[isect], b[isect]], axis=0)
|
|
40
|
+
o = spearmanr(combined_df, axis=1)
|
|
41
|
+
|
|
42
|
+
a_count = a.shape[0]
|
|
43
|
+
sdf = pd.DataFrame(o.correlation[a_count:, :a_count], index=b.index, columns=a.index)
|
|
44
|
+
|
|
45
|
+
rows, cols = linear_sum_assignment(-sdf)
|
|
46
|
+
|
|
47
|
+
out_a = []
|
|
48
|
+
out_b = []
|
|
49
|
+
out_score = []
|
|
50
|
+
|
|
51
|
+
for r, c in zip(rows, cols):
|
|
52
|
+
score = sdf.iloc[r, c]
|
|
53
|
+
if score >= cutoff:
|
|
54
|
+
out_a.append(a.index[c])
|
|
55
|
+
out_b.append(b.index[r])
|
|
56
|
+
out_score.append(score)
|
|
57
|
+
|
|
58
|
+
return out_a, out_b, out_score
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def matrix_spearman_alignment_hopkraft(a, b, cutoff=0.0):
|
|
62
|
+
"""
|
|
63
|
+
matrix_spearman_alignment_hopkraft
|
|
64
|
+
|
|
65
|
+
Uses the HopcroftKarp maximum *cardinality* matching algorithm.
|
|
66
|
+
It finds the LARGEST NUMBER of matches, not the BEST (highest score)
|
|
67
|
+
match.
|
|
68
|
+
|
|
69
|
+
Note: This is fundamentally different from linear_sum_assignment.
|
|
70
|
+
"""
|
|
71
|
+
isect = a.columns.intersection(b.columns)
|
|
72
|
+
|
|
73
|
+
combined_df = pd.concat([a[isect], b[isect]], axis=0)
|
|
74
|
+
o = spearmanr(combined_df, axis=1)
|
|
75
|
+
|
|
76
|
+
a_count = a.shape[0]
|
|
77
|
+
sdf = pd.DataFrame(o.correlation[a_count:, :a_count], index=b.index, columns=a.index)
|
|
78
|
+
|
|
79
|
+
m = {}
|
|
80
|
+
s_result = sdf.apply(lambda x: x.nlargest(10).index, axis=0)
|
|
81
|
+
for k, row in s_result.items():
|
|
82
|
+
m[k] = row.tolist()
|
|
83
|
+
|
|
84
|
+
id_map = HopcroftKarp(m).maximum_matching(keys_only=True)
|
|
85
|
+
|
|
86
|
+
out_a = []
|
|
87
|
+
out_b = []
|
|
88
|
+
out_score = []
|
|
89
|
+
|
|
90
|
+
for k, v in id_map.items():
|
|
91
|
+
c = sdf.loc[v, k]
|
|
92
|
+
if c >= cutoff:
|
|
93
|
+
out_a.append(k)
|
|
94
|
+
out_b.append(v)
|
|
95
|
+
out_score.append(c)
|
|
96
|
+
|
|
97
|
+
return out_a, out_b, out_score
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def procrustes(X, Y):
|
|
101
|
+
"""
|
|
102
|
+
Computes the Procrustes transformation (optimal PURE rotation)
|
|
103
|
+
to align X to Y, correcting for reflections.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
X: The first matrix (N_points, N_dims).
|
|
107
|
+
Y: The second matrix (N_points, N_dims).
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
R: The optimal rotation matrix (guaranteed det(R) = +1).
|
|
111
|
+
"""
|
|
112
|
+
assert X.shape == Y.shape
|
|
113
|
+
|
|
114
|
+
M = np.dot(X.T, Y)
|
|
115
|
+
|
|
116
|
+
U, S, Vt = np.linalg.svd(M)
|
|
117
|
+
|
|
118
|
+
R = np.dot(U, Vt)
|
|
119
|
+
|
|
120
|
+
if np.linalg.det(R) < 0:
|
|
121
|
+
Vt_corrected = Vt.copy()
|
|
122
|
+
Vt_corrected[-1, :] *= -1
|
|
123
|
+
R = np.dot(U, Vt_corrected)
|
|
124
|
+
|
|
125
|
+
return R
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def procrustes_scale(X, Y):
|
|
129
|
+
"""
|
|
130
|
+
Compute the procrustes transformation (R), then compute a factor (k) to rescale the src matrix.
|
|
131
|
+
|
|
132
|
+
To apply transformtion:
|
|
133
|
+
src.dot(R) * k
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
X: The first matrix (N_points, N_dims).
|
|
138
|
+
Y: The second matrix (N_points, N_dims).
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
R: The optimal rotation matrix (guaranteed det(R) = +1).
|
|
142
|
+
k: Scaling factor
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
R = procrustes(X,Y)
|
|
146
|
+
A = np.array(X.dot(R))
|
|
147
|
+
B = np.array(Y)
|
|
148
|
+
numerators = np.sum(A * B, axis=0)
|
|
149
|
+
denominators = np.sum(A * A, axis=0)
|
|
150
|
+
k = np.divide(numerators, denominators, out=np.zeros_like(numerators), where=denominators!=0)
|
|
151
|
+
return R, k
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .api import CBIOAPI
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CBIOAPI:
|
|
5
|
+
|
|
6
|
+
@staticmethod
|
|
7
|
+
def list_studies():
|
|
8
|
+
try:
|
|
9
|
+
studies = requests.get("https://www.cbioportal.org/api/studies").json()
|
|
10
|
+
return studies
|
|
11
|
+
except requests.RequestException as e:
|
|
12
|
+
print(f"Error fetching studies: {e}")
|
|
13
|
+
return None
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
"""
|
|
3
|
+
Command Line Methods
|
|
4
|
+
|
|
5
|
+
embkit matrix <> : Matrix methods for normalization and transformation
|
|
6
|
+
|
|
7
|
+
embkit model <> : Methods of model training and application
|
|
8
|
+
|
|
9
|
+
embkit cbio <> : cBIO methods for data querying and download
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .model import model
|
|
14
|
+
from .matrix import matrix
|
|
15
|
+
from .cbio import cbio_cmd
|
|
16
|
+
from .datasets import datasets
|
|
17
|
+
from .protein import protein
|
|
18
|
+
from .align import align
|
|
19
|
+
|
|
20
|
+
__all__ = ["model", "matrix", "datasets", "protein"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
Command line tools for aligning embedding spaces
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from ..align import matrix_spearman_alignment_linear, matrix_spearman_alignment_hopkraft
|
|
10
|
+
|
|
11
|
+
align = click.Group(name="align", help="Embedding Alignment methods")
|
|
12
|
+
|
|
13
|
+
@align.command(name="pair")
|
|
14
|
+
@click.argument("matrix1")
|
|
15
|
+
@click.argument("matrix2")
|
|
16
|
+
@click.option("--method", "-m", required=False, type=str, default="linear")
|
|
17
|
+
@click.option("--cutoff", "-c", required=False, type=float, default=0.5)
|
|
18
|
+
def pair(matrix1, matrix2, method, cutoff):
|
|
19
|
+
"""
|
|
20
|
+
Given two matrices, use feature rank correlation to
|
|
21
|
+
create list of pairs
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
m1 = pd.read_csv(matrix1, sep="\t", index_col=0)
|
|
25
|
+
m2 = pd.read_csv(matrix2, sep="\t", index_col=0)
|
|
26
|
+
|
|
27
|
+
if method == "hopkraft":
|
|
28
|
+
out = matrix_spearman_alignment_hopkraft(m1, m2)
|
|
29
|
+
for k, v in out.items():
|
|
30
|
+
print(f"{k}\t{v[0]}\t{v[1]}")
|
|
31
|
+
|
|
32
|
+
elif method == "linear":
|
|
33
|
+
out = matrix_spearman_alignment_linear(m1, m2, cutoff)
|
|
34
|
+
for k, v in out.items():
|
|
35
|
+
print(f"{k}\t{v[0]}\t{v[1]}")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from ..datasets import CBIOPortal
|
|
3
|
+
from ..c_bio import CBIOAPI
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
cbio_cmd = click.Group(name="cbio", help="cBIO commands.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@cbio_cmd.command(name="studies", help="List cbio studies.")
|
|
12
|
+
@click.pass_context
|
|
13
|
+
def list_studies(ctx):
|
|
14
|
+
"""
|
|
15
|
+
Command to list available cBIO studies.
|
|
16
|
+
|
|
17
|
+
Parameters:
|
|
18
|
+
ctx: Click context object.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
None
|
|
22
|
+
"""
|
|
23
|
+
api = CBIOAPI()
|
|
24
|
+
studies = api.list_studies()
|
|
25
|
+
|
|
26
|
+
if studies:
|
|
27
|
+
for study in studies:
|
|
28
|
+
click.echo(f"Study ID: {study['studyId']}, Name: {study['name']}")
|
|
29
|
+
else:
|
|
30
|
+
click.echo("No studies found or an error occurred while fetching studies.")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@cbio_cmd.command(name="download", help="Download cbio dataset.")
|
|
34
|
+
@click.option("--save_path", "-s", type=click.Path(exists=False, writable=True, path_type=str),
|
|
35
|
+
help="Path to save the dataset.")
|
|
36
|
+
@click.option("--study_id", "-si", type=str, show_default=True, help="Id of the study to download.")
|
|
37
|
+
@click.pass_context
|
|
38
|
+
def download_tmp(ctx, save_path: str | None, study_id: str):
|
|
39
|
+
"""
|
|
40
|
+
Command to download a cBIO dataset by specifying the study ID.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Parameters:
|
|
44
|
+
ctx: Click context object.
|
|
45
|
+
save_path: Path to save the dataset.
|
|
46
|
+
study_id: ID of the study to download.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
None
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
if study_id is None:
|
|
53
|
+
click.echo("Study Id not specified. Please use -si option to specify the study id.")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
cbio_portal: CBIOPortal = CBIOPortal(save_path=save_path, study_id=study_id, download=True)
|
|
57
|
+
cbio_portal.download()
|
|
58
|
+
cbio_portal.unpack()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# needed for testing
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
cbio_cmd() # pragma: no cover
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from ..preprocessing import load_raw_hugo, load_gct
|
|
4
|
+
from ..datasets import GTEx, Hugo, SIF
|
|
5
|
+
|
|
6
|
+
datasets = click.Group(name="datasets", help="Datasets commands.")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@datasets.command()
|
|
10
|
+
@click.option("--data_type", "-t", required=False, type=str, help="Name of the dataset to download.")
|
|
11
|
+
@click.option("--output_folder", "-f", required=True, type=str, help="The folder to download the dataset into.")
|
|
12
|
+
def gtex(data_type: str, output_folder: str):
|
|
13
|
+
"""Download GTEx dataset."""
|
|
14
|
+
|
|
15
|
+
if data_type not in GTEx.NAMES:
|
|
16
|
+
click.echo(f"Dataset name '{data_type}' is not recognized. Available options are: {list(GTEx.NAMES.keys())}")
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
GTEx(data_type=data_type, save_path=output_folder)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@datasets.command()
|
|
24
|
+
@click.option("--output_folder", "-f", required=True, type=str, help="The folder to download the dataset into.")
|
|
25
|
+
def sif(output_folder: str):
|
|
26
|
+
"""Download SIF dataset."""
|
|
27
|
+
SIF(save_path=output_folder)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@datasets.command()
|
|
32
|
+
@click.option("--output_folder", "-f", required=True, type=str, help="The folder to download the dataset into.")
|
|
33
|
+
@click.option("--gtex_conversion", "-gtex", required=False, is_flag=True, type=bool, default=False,
|
|
34
|
+
help="Whether to convert the hugo data to a gtex version.")
|
|
35
|
+
def hugo(gtex_conversion: bool, output_folder: str):
|
|
36
|
+
"""Download Hugo dataset."""
|
|
37
|
+
|
|
38
|
+
click.echo(f"Downloading hugo dataset into '{output_folder}'")
|
|
39
|
+
hugo_data: Hugo = Hugo(save_path=output_folder)
|
|
40
|
+
|
|
41
|
+
if gtex_conversion:
|
|
42
|
+
click.echo("Converting hugo dataset using GTEx dataset...")
|
|
43
|
+
click.echo("Downloading required dataset (GTEx)...")
|
|
44
|
+
gtex_data: GTEx = GTEx(save_path=output_folder)
|
|
45
|
+
click.echo("Loading GTEx dataset...")
|
|
46
|
+
gtex_df: pd.DataFrame = load_gct(gtex_data)
|
|
47
|
+
click.echo("Loading Hugo dataset...")
|
|
48
|
+
hugo_df: pd.DataFrame = load_raw_hugo(hugo_data)
|
|
49
|
+
|
|
50
|
+
# select protein coding genes and build a dict that translates from ensembl gene id to Hugo name
|
|
51
|
+
gene_names = hugo_df[hugo_df["locus_group"] == "protein-coding gene"][["symbol", "ensembl_gene_id"]].set_index(
|
|
52
|
+
"ensembl_gene_id")["symbol"].to_dict()
|
|
53
|
+
|
|
54
|
+
# rename columns using HUGO names
|
|
55
|
+
df_select = gtex_df.rename(columns=lambda x: gene_names.get(x.split(".")[0], x))
|
|
56
|
+
# remove columns that aren't renamed (including all non-protein coding columns)
|
|
57
|
+
df_select = df_select.loc[:, df_select.columns.map(lambda x: not x.startswith("ENSG0"))]
|
|
58
|
+
output_path = f"{hugo_data.save_path}/gtex.hugo.tsv"
|
|
59
|
+
click.echo(f"Saving converted dataset to '{output_path}...'")
|
|
60
|
+
df_select.to_csv(output_path, sep="\t")
|
|
61
|
+
click.echo("Done.")
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
matrix = click.Group(name="matrix", help="Model commands.")
|
|
5
|
+
|
|
6
|
+
@matrix.command()
|
|
7
|
+
@click.argument("srcs", nargs=-1)
|
|
8
|
+
@click.option("--out", "-o", required=True)
|
|
9
|
+
@click.option("--features", default=None, type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str))
|
|
10
|
+
@click.option("--col-quantile", "col_quantile", default=False, is_flag=True)
|
|
11
|
+
@click.option("--quantile-max", "quantile_max", default=0.9, type=float)
|
|
12
|
+
@click.option("--precision", "-p", type=int, default=5)
|
|
13
|
+
def normalize(srcs, out, features, col_quantile, quantile_max, precision):
|
|
14
|
+
|
|
15
|
+
features_list = None
|
|
16
|
+
if features is not None:
|
|
17
|
+
features = []
|
|
18
|
+
with open(features, encoding="ascii") as handle:
|
|
19
|
+
features_list = list(line.rstrip() for line in handle)
|
|
20
|
+
|
|
21
|
+
dfs = []
|
|
22
|
+
for i in srcs:
|
|
23
|
+
df = pd.read_csv(i, sep="\t", index_col=0)
|
|
24
|
+
if features is not None:
|
|
25
|
+
df = df[features]
|
|
26
|
+
dfs.append(df)
|
|
27
|
+
if len(dfs) == 0:
|
|
28
|
+
click.echo("No matrices defined")
|
|
29
|
+
return
|
|
30
|
+
df = pd.concat( dfs )
|
|
31
|
+
|
|
32
|
+
if not col_quantile:
|
|
33
|
+
normDF = (df.transpose() / df.quantile(quantile_max, axis=1)).transpose().clip(upper=1.0, lower=0.0).fillna(0.0)
|
|
34
|
+
else:
|
|
35
|
+
normDF = (df / df.quantile(quantile_max)).clip(upper=1.0, lower=0.0).fillna(0.0)
|
|
36
|
+
normDF.round(decimals=precision).to_csv(out, sep="\t")
|