embkit 0.2__tar.gz → 0.4__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 → embkit-0.4}/PKG-INFO +3 -1
- {embkit-0.2 → embkit-0.4}/pyproject.toml +5 -3
- {embkit-0.2 → embkit-0.4}/setup.py +1 -1
- {embkit-0.2 → embkit-0.4}/src/embkit/__init__.py +23 -8
- {embkit-0.2 → embkit-0.4}/src/embkit/__main__.py +14 -4
- {embkit-0.2 → embkit-0.4}/src/embkit/align.py +0 -41
- {embkit-0.2 → embkit-0.4}/src/embkit/c_bio/api.py +4 -1
- {embkit-0.2 → embkit-0.4}/src/embkit/commands/__init__.py +2 -2
- {embkit-0.2 → embkit-0.4}/src/embkit/commands/align.py +5 -10
- {embkit-0.2 → embkit-0.4}/src/embkit/commands/cbio.py +1 -1
- {embkit-0.2 → embkit-0.4}/src/embkit/commands/matrix.py +21 -3
- embkit-0.4/src/embkit/commands/model.py +401 -0
- embkit-0.4/src/embkit/commands/protein.py +75 -0
- embkit-0.2/src/embkit/commands/datasets.py → embkit-0.4/src/embkit/commands/resources.py +6 -6
- embkit-0.4/src/embkit/constraints/__init__.py +7 -0
- embkit-0.4/src/embkit/constraints/pathway_constraint.py +110 -0
- embkit-0.4/src/embkit/datasets/__init__.py +51 -0
- embkit-0.4/src/embkit/encoding/__init__.py +201 -0
- embkit-0.4/src/embkit/encoding/genome.py +74 -0
- embkit-0.4/src/embkit/encoding/protein.py +98 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/estimator/vae_estimator.py +2 -2
- embkit-0.4/src/embkit/factory/__init__.py +17 -0
- embkit-0.4/src/embkit/factory/core.py +100 -0
- embkit-0.4/src/embkit/factory/layers.py +178 -0
- embkit-0.4/src/embkit/factory/mapping.py +117 -0
- embkit-0.4/src/embkit/factory/registry.py +71 -0
- embkit-0.4/src/embkit/files/__init__.py +3 -0
- embkit-0.4/src/embkit/files/h5.py +122 -0
- embkit-0.4/src/embkit/files/json.py +20 -0
- {embkit-0.2/src/embkit/file_readers → embkit-0.4/src/embkit/files}/read_csv.py +85 -56
- {embkit-0.2 → embkit-0.4}/src/embkit/losses/vae_loss.py +14 -8
- embkit-0.4/src/embkit/models/ffnn.py +127 -0
- embkit-0.4/src/embkit/models/vae/base_vae.py +240 -0
- embkit-0.4/src/embkit/models/vae/decoder.py +97 -0
- embkit-0.4/src/embkit/models/vae/encoder.py +180 -0
- embkit-0.4/src/embkit/models/vae/net_vae.py +292 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/models/vae/rna_vae.py +67 -13
- embkit-0.4/src/embkit/models/vae/vae.py +111 -0
- embkit-0.4/src/embkit/modules/__init__.py +18 -0
- {embkit-0.2/src/embkit/layers → embkit-0.4/src/embkit/modules}/masked_linear.py +19 -4
- {embkit-0.2/src/embkit/layers → embkit-0.4/src/embkit/modules}/tsp.py +12 -8
- embkit-0.4/src/embkit/optimize/__init__.py +497 -0
- embkit-0.4/src/embkit/pathway.py +227 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/preprocessing/__init__.py +2 -2
- embkit-0.4/src/embkit/preprocessing/dataset.py +35 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/preprocessing/normalize.py +37 -6
- {embkit-0.2/src/embkit/datasets → embkit-0.4/src/embkit/resources}/c_bio_portal.py +4 -3
- {embkit-0.2/src/embkit/datasets → embkit-0.4/src/embkit/resources}/gtex.py +2 -2
- {embkit-0.2/src/embkit/datasets → embkit-0.4/src/embkit/resources}/hugo.py +1 -1
- embkit-0.2/src/embkit/datasets/dataset.py → embkit-0.4/src/embkit/resources/resource.py +39 -22
- {embkit-0.2/src/embkit/datasets → embkit-0.4/src/embkit/resources}/sif.py +1 -1
- {embkit-0.2 → embkit-0.4}/src/embkit/utilities/kmeans.py +1 -1
- {embkit-0.2 → embkit-0.4}/src/embkit/utilities/pca.py +8 -5
- embkit-0.4/src/embkit/version.py +62 -0
- {embkit-0.2 → embkit-0.4}/src/embkit.egg-info/PKG-INFO +3 -1
- {embkit-0.2 → embkit-0.4}/src/embkit.egg-info/SOURCES.txt +29 -21
- {embkit-0.2 → embkit-0.4}/src/embkit.egg-info/requires.txt +2 -0
- {embkit-0.2 → embkit-0.4}/tests/test_main.py +29 -0
- embkit-0.2/src/embkit/commands/model.py +0 -220
- embkit-0.2/src/embkit/commands/protein.py +0 -56
- embkit-0.2/src/embkit/constraints/__init__.py +0 -1
- embkit-0.2/src/embkit/constraints/network_constraint.py +0 -54
- embkit-0.2/src/embkit/file_readers/__init__.py +0 -1
- embkit-0.2/src/embkit/layers/__init__.py +0 -33
- embkit-0.2/src/embkit/layers/constraint_info.py +0 -111
- embkit-0.2/src/embkit/layers/layer_info.py +0 -108
- embkit-0.2/src/embkit/models/ffnn.py +0 -151
- embkit-0.2/src/embkit/models/hla2vec.py +0 -307
- embkit-0.2/src/embkit/models/protein.py +0 -175
- embkit-0.2/src/embkit/models/vae/base_vae.py +0 -327
- embkit-0.2/src/embkit/models/vae/decoder.py +0 -58
- embkit-0.2/src/embkit/models/vae/encoder.py +0 -145
- embkit-0.2/src/embkit/models/vae/hla2vec.py +0 -307
- embkit-0.2/src/embkit/models/vae/net_vae.py +0 -192
- embkit-0.2/src/embkit/models/vae/pytorch.py +0 -150
- embkit-0.2/src/embkit/models/vae/vae.py +0 -218
- embkit-0.2/src/embkit/pathway.py +0 -177
- {embkit-0.2 → embkit-0.4}/README.md +0 -0
- {embkit-0.2 → embkit-0.4}/setup.cfg +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/bmeg.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/c_bio/__init__.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/estimator/__init__.py +0 -0
- {embkit-0.2/src/embkit/preprocessing → embkit-0.4/src/embkit/files}/loaders.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/losses/__init__.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/metrics/__init__.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/metrics/distance/__init__.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/metrics/distance/metrics.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/models/__init__.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/models/vae/__init__.py +0 -0
- {embkit-0.2/src/embkit/layers → embkit-0.4/src/embkit/modules}/pairwise_comparison_layer.py +0 -0
- {embkit-0.2/src/embkit/datasets → embkit-0.4/src/embkit/resources}/__init__.py +0 -0
- {embkit-0.2/src/embkit/datasets → embkit-0.4/src/embkit/resources}/util.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit/utilities/__init__.py +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit.egg-info/dependency_links.txt +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit.egg-info/entry_points.txt +0 -0
- {embkit-0.2 → embkit-0.4}/src/embkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: embkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4
|
|
4
4
|
Summary: Embedding Kit for embedding models
|
|
5
5
|
Author-email: Kyle Ellrott <ellrott@ohsu.edu>, Raphael Kirchgaessner <kirchgae@ohsu.edu>
|
|
6
6
|
License: MIT
|
|
@@ -17,6 +17,8 @@ Requires-Dist: gripql
|
|
|
17
17
|
Requires-Dist: tqdm
|
|
18
18
|
Requires-Dist: biopython
|
|
19
19
|
Requires-Dist: fair-esm
|
|
20
|
+
Requires-Dist: faiss-cpu
|
|
21
|
+
Requires-Dist: h5py
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
# Embedding Kit
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "embkit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4"
|
|
8
8
|
description = "Embedding Kit for embedding models"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -24,7 +24,9 @@ dependencies = [
|
|
|
24
24
|
"gripql",
|
|
25
25
|
"tqdm",
|
|
26
26
|
"biopython",
|
|
27
|
-
"fair-esm"
|
|
27
|
+
"fair-esm",
|
|
28
|
+
"faiss-cpu",
|
|
29
|
+
"h5py"
|
|
28
30
|
]
|
|
29
31
|
|
|
30
32
|
[tool.setuptools]
|
|
@@ -34,4 +36,4 @@ package-dir = { "" = "src" }
|
|
|
34
36
|
where = ["src"]
|
|
35
37
|
|
|
36
38
|
[project.scripts]
|
|
37
|
-
embkit = "embkit.__main__:cli"
|
|
39
|
+
embkit = "embkit.__main__:cli"
|
|
@@ -24,7 +24,7 @@ def get_device() -> torch.device:
|
|
|
24
24
|
logger.info("Using CPU")
|
|
25
25
|
return torch.device("cpu")
|
|
26
26
|
|
|
27
|
-
def dataframe_tensor(df: pd.DataFrame,device=None) -> torch.Tensor:
|
|
27
|
+
def dataframe_tensor(df: pd.DataFrame, device=None, dtype=torch.float32) -> torch.Tensor:
|
|
28
28
|
"""
|
|
29
29
|
dataframe_tensor
|
|
30
30
|
|
|
@@ -32,7 +32,7 @@ def dataframe_tensor(df: pd.DataFrame,device=None) -> torch.Tensor:
|
|
|
32
32
|
"""
|
|
33
33
|
if device is None:
|
|
34
34
|
device = get_device()
|
|
35
|
-
return torch.from_numpy(df.
|
|
35
|
+
return torch.from_numpy(df.to_numpy(copy=True)).to(device=device, dtype=dtype)
|
|
36
36
|
|
|
37
37
|
def tensor_dataframe(tn: torch.Tensor, index=None, columns=None) -> pd.DataFrame:
|
|
38
38
|
"""
|
|
@@ -40,11 +40,29 @@ def tensor_dataframe(tn: torch.Tensor, index=None, columns=None) -> pd.DataFrame
|
|
|
40
40
|
|
|
41
41
|
take a torch tensor, create a pandas dataframe
|
|
42
42
|
"""
|
|
43
|
-
return pd.DataFrame(tn.cpu
|
|
43
|
+
return pd.DataFrame(tn.to(device="cpu", dtype=torch.float32).detach().numpy(), index=index, columns=columns)
|
|
44
|
+
|
|
45
|
+
def dataframe_dataset(df: pd.DataFrame, device=None, dtype=torch.float32) -> torch.utils.data.Dataset:
|
|
46
|
+
"""
|
|
47
|
+
dataframe_dataset
|
|
48
|
+
|
|
49
|
+
take a pandas dataframe and return a pytorch dataset
|
|
50
|
+
|
|
51
|
+
:param df: Input pandas Dataframe
|
|
52
|
+
:type df: pd.DataFrame
|
|
53
|
+
:param device: Device to put torch tensors on
|
|
54
|
+
:return: Description
|
|
55
|
+
:rtype: Dataset
|
|
56
|
+
"""
|
|
57
|
+
if device is None:
|
|
58
|
+
device = get_device()
|
|
59
|
+
x = torch.from_numpy(df.to_numpy(copy=True)).to(device=device, dtype=dtype)
|
|
60
|
+
dataset = TensorDataset(x)
|
|
61
|
+
return dataset
|
|
44
62
|
|
|
45
63
|
def dataframe_loader(df: pd.DataFrame,
|
|
46
64
|
batch_size = 256, shuffle=True,
|
|
47
|
-
device=None) -> torch.utils.data.DataLoader:
|
|
65
|
+
device=None, dtype=torch.float32) -> torch.utils.data.DataLoader:
|
|
48
66
|
"""
|
|
49
67
|
dataframe_loader
|
|
50
68
|
|
|
@@ -54,9 +72,6 @@ def dataframe_loader(df: pd.DataFrame,
|
|
|
54
72
|
:param device: Device the where tensor should be moved. If None, use get_device
|
|
55
73
|
"""
|
|
56
74
|
|
|
57
|
-
|
|
58
|
-
device = get_device()
|
|
59
|
-
x = torch.from_numpy(df.values.astype(np.float32)).to(device)
|
|
60
|
-
dataset = TensorDataset(x)
|
|
75
|
+
dataset = dataframe_dataset(df, device=device, dtype=dtype)
|
|
61
76
|
data_loader = DataLoader(dataset, batch_size=batch_size, shuffle=shuffle)
|
|
62
77
|
return data_loader
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import click
|
|
2
2
|
import logging
|
|
3
3
|
|
|
4
|
-
from .
|
|
4
|
+
from embkit.version import get_version
|
|
5
|
+
|
|
6
|
+
from .commands import model, matrix, cbio_cmd, protein, resources, align
|
|
5
7
|
|
|
6
8
|
@click.group(invoke_without_command=True)
|
|
7
9
|
@click.option('--verbose', is_flag=True, help="Enable verbose logging")
|
|
10
|
+
@click.option('--version', is_flag=True, help="Show version information")
|
|
8
11
|
@click.pass_context
|
|
9
|
-
def cli(ctx, verbose):
|
|
12
|
+
def cli(ctx, verbose, version):
|
|
10
13
|
"""Embedding Kit CLI.
|
|
11
14
|
|
|
12
15
|
Use 'embkit help [COMMAND]' for details on a specific command.
|
|
@@ -20,6 +23,10 @@ def cli(ctx, verbose):
|
|
|
20
23
|
ctx.ensure_object(dict)
|
|
21
24
|
ctx.obj["VERBOSE"] = verbose
|
|
22
25
|
|
|
26
|
+
if version:
|
|
27
|
+
click.echo(get_version())
|
|
28
|
+
ctx.exit()
|
|
29
|
+
|
|
23
30
|
if ctx.invoked_subcommand is None:
|
|
24
31
|
click.echo(ctx.get_help())
|
|
25
32
|
|
|
@@ -29,7 +36,7 @@ cli.add_command(model)
|
|
|
29
36
|
cli.add_command(matrix)
|
|
30
37
|
cli.add_command(cbio_cmd)
|
|
31
38
|
cli.add_command(protein)
|
|
32
|
-
cli.add_command(
|
|
39
|
+
cli.add_command(resources)
|
|
33
40
|
cli.add_command(align)
|
|
34
41
|
|
|
35
42
|
|
|
@@ -62,11 +69,14 @@ def help_cmd(ctx, path):
|
|
|
62
69
|
info_name = []
|
|
63
70
|
parent = ctx.parent # start from top-level context
|
|
64
71
|
for part in path:
|
|
65
|
-
if not
|
|
72
|
+
if not isinstance(cmd, click.Group):
|
|
66
73
|
click.echo(cli.get_help(ctx)) # pragma: no cover
|
|
74
|
+
return
|
|
75
|
+
|
|
67
76
|
nxt_cmd = cmd.get_command(parent, part)
|
|
68
77
|
if nxt_cmd is None:
|
|
69
78
|
raise click.UsageError(f"Unknown command: {' '.join(path)}")
|
|
79
|
+
|
|
70
80
|
info_name.append(part)
|
|
71
81
|
cmd = nxt_cmd
|
|
72
82
|
parent = click.Context(cmd, info_name=part, parent=parent)
|
|
@@ -2,8 +2,6 @@ import numpy as np
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
from scipy.stats import spearmanr
|
|
4
4
|
from scipy.optimize import linear_sum_assignment
|
|
5
|
-
from hopcroftkarp import HopcroftKarp
|
|
6
|
-
|
|
7
5
|
|
|
8
6
|
def calc_rmsd(array1, array2):
|
|
9
7
|
"""
|
|
@@ -58,45 +56,6 @@ def matrix_spearman_alignment_linear(a, b, cutoff=0.0):
|
|
|
58
56
|
return out_a, out_b, out_score
|
|
59
57
|
|
|
60
58
|
|
|
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
59
|
def procrustes(X, Y):
|
|
101
60
|
"""
|
|
102
61
|
Computes the Procrustes transformation (optimal PURE rotation)
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import requests
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger(__name__)
|
|
2
5
|
|
|
3
6
|
|
|
4
7
|
class CBIOAPI:
|
|
@@ -9,5 +12,5 @@ class CBIOAPI:
|
|
|
9
12
|
studies = requests.get("https://www.cbioportal.org/api/studies").json()
|
|
10
13
|
return studies
|
|
11
14
|
except requests.RequestException as e:
|
|
12
|
-
|
|
15
|
+
logger.error("Error fetching studies: %s", e)
|
|
13
16
|
return None
|
|
@@ -13,8 +13,8 @@ embkit cbio <> : cBIO methods for data querying and download
|
|
|
13
13
|
from .model import model
|
|
14
14
|
from .matrix import matrix
|
|
15
15
|
from .cbio import cbio_cmd
|
|
16
|
-
from .
|
|
16
|
+
from .resources import resources
|
|
17
17
|
from .protein import protein
|
|
18
18
|
from .align import align
|
|
19
19
|
|
|
20
|
-
__all__ = ["model", "matrix", "
|
|
20
|
+
__all__ = ["model", "matrix", "resources", "protein"]
|
|
@@ -6,7 +6,7 @@ Command line tools for aligning embedding spaces
|
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
8
|
import pandas as pd
|
|
9
|
-
from ..align import matrix_spearman_alignment_linear
|
|
9
|
+
from ..align import matrix_spearman_alignment_linear
|
|
10
10
|
|
|
11
11
|
align = click.Group(name="align", help="Embedding Alignment methods")
|
|
12
12
|
|
|
@@ -24,12 +24,7 @@ def pair(matrix1, matrix2, method, cutoff):
|
|
|
24
24
|
m1 = pd.read_csv(matrix1, sep="\t", index_col=0)
|
|
25
25
|
m2 = pd.read_csv(matrix2, sep="\t", index_col=0)
|
|
26
26
|
|
|
27
|
-
if method == "
|
|
28
|
-
|
|
29
|
-
for
|
|
30
|
-
|
|
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]}")
|
|
27
|
+
if method == "linear":
|
|
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}")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import click
|
|
2
2
|
import pandas as pd
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
matrix = click.Group(name="matrix", help="Model commands.")
|
|
5
6
|
|
|
@@ -14,15 +15,14 @@ def normalize(srcs, out, features, col_quantile, quantile_max, precision):
|
|
|
14
15
|
|
|
15
16
|
features_list = None
|
|
16
17
|
if features is not None:
|
|
17
|
-
features = []
|
|
18
18
|
with open(features, encoding="ascii") as handle:
|
|
19
19
|
features_list = list(line.rstrip() for line in handle)
|
|
20
20
|
|
|
21
21
|
dfs = []
|
|
22
22
|
for i in srcs:
|
|
23
23
|
df = pd.read_csv(i, sep="\t", index_col=0)
|
|
24
|
-
if
|
|
25
|
-
df = df[
|
|
24
|
+
if features_list is not None:
|
|
25
|
+
df = df[features_list]
|
|
26
26
|
dfs.append(df)
|
|
27
27
|
if len(dfs) == 0:
|
|
28
28
|
click.echo("No matrices defined")
|
|
@@ -34,3 +34,21 @@ def normalize(srcs, out, features, col_quantile, quantile_max, precision):
|
|
|
34
34
|
else:
|
|
35
35
|
normDF = (df / df.quantile(quantile_max)).clip(upper=1.0, lower=0.0).fillna(0.0)
|
|
36
36
|
normDF.round(decimals=precision).to_csv(out, sep="\t")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@matrix.command()
|
|
40
|
+
@click.argument("input_path", type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str))
|
|
41
|
+
@click.option("--pca-size", required=True, type=int, help="Number of principal components.")
|
|
42
|
+
@click.option("--out", "-o", default=None, type=str, help="Output TSV path.")
|
|
43
|
+
def pca(input_path, pca_size, out):
|
|
44
|
+
if pca_size <= 0:
|
|
45
|
+
raise click.BadParameter("--pca-size must be a positive integer.")
|
|
46
|
+
|
|
47
|
+
from ..utilities.pca import run_pca
|
|
48
|
+
|
|
49
|
+
if out is None:
|
|
50
|
+
out = f"{Path(input_path).stem}.pca.tsv"
|
|
51
|
+
click.echo(f"No output path provided, using default naming: {out}")
|
|
52
|
+
|
|
53
|
+
run_pca(input_file=input_path, pca_size=pca_size, output_file=out)
|
|
54
|
+
click.echo(f"PCA saved to {out}")
|