Repository navigation
Add PaCMAP dimensionality reduction (closes #173) #193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
| import numpy as np | ||
| import pandas as pd | ||
| from openTSNE import TSNE | ||
| from pacmap import PaCMAP | ||
| from scipy.stats import energy_distance, pearsonr, spearmanr, wasserstein_distance | ||
| from sklearn import decomposition | ||
| from sklearn.metrics import DistanceMetric | ||
|
|
@@ -260,6 +261,40 @@ def calculate_umap(self, n_components: int = 2, standardise: bool = True, **kwar | |
| umap = UMAP(n_components=n_components, **kwargs) | ||
| return umap.fit_transform(embeddings_array) | ||
|
|
||
| def calculate_pacmap( | ||
| self, | ||
| n_components: int = 2, | ||
| standardise: bool = True, | ||
| init: str = "pca", | ||
| **kwargs, | ||
| ): | ||
| """Calculate PaCMAP (Pairwise Controlled Manifold Approximation) embeddings. | ||
|
|
||
| Args: | ||
| ---- | ||
| n_components (int): The number of components to project the embeddings to. | ||
| standardise (bool): Whether to scale the embeddings before projecting. | ||
| init (str): Initialisation of the low-dimensional embedding, passed to | ||
| ``PaCMAP.fit_transform`` (e.g. ``"pca"`` or ``"random"``). | ||
| **kwargs: Other keyword arguments to be passed to PaCMAP. | ||
| """ | ||
| if standardise: | ||
| if self.is_standardised: | ||
| embeddings_array = np.array(list(self.embeddings.values())) | ||
| else: | ||
| self.standardise(inplace=True) | ||
| embeddings_array = np.array(list(self.embeddings.values())) | ||
| else: | ||
| warnings.warn( | ||
| """It is recommended to scale the embeddings | ||
| before projecting with PaCMAP. | ||
| To do so, set `standardise=True`.""", | ||
| ) | ||
|
Comment on lines
+288
to
+292
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The warning mirrors the wording and triple-quoted format of the existing
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| embeddings_array = np.array(list(self.embeddings.values())) | ||
|
|
||
| pacmap = PaCMAP(n_components=n_components, **kwargs) | ||
| return pacmap.fit_transform(embeddings_array, init=init) | ||
|
|
||
| def compute_correlation_metric( | ||
| self, | ||
| el_sp1: str, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,7 +115,8 @@ def dimension_plotter( | |
| embedding (Embedding): The embedding to be plotted. | ||
| ax (plt.axes, optional): The axes to plot on, by default None | ||
| n_components (int): The number of components to reduce to, by default 2 | ||
| reducer (str): The dimensionality reduction algorithm to use, by default "umap" | ||
| reducer (str): The dimensionality reduction algorithm to use. One of | ||
| "umap", "tsne", "pca" or "pacmap", by default "umap" | ||
|
Comment on lines
+118
to
+119
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 6089661 — the dispatch |
||
| adjusttext (bool): Whether to avoid overlap of the text labels, by default True | ||
| reducer_params (dict, optional): Additional keyword arguments to pass to | ||
| the reducer, by default None | ||
|
|
@@ -132,8 +133,10 @@ def dimension_plotter( | |
| reduced = embedding.calculate_tsne(n_components=n_components, **reducer_params) | ||
| elif reducer == "pca": | ||
| reduced = embedding.calculate_pca(n_components=n_components, **reducer_params) | ||
| elif reducer == "pacmap": | ||
| reduced = embedding.calculate_pacmap(n_components=n_components, **reducer_params) | ||
| else: | ||
| msg = "Unrecognised reducer." | ||
| msg = f"Unrecognised reducer: {reducer!r}. Expected one of 'umap', 'tsne', 'pca' or 'pacmap'." | ||
| raise ValueError(msg) | ||
|
|
||
| if isinstance(embedding, Embedding): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -474,6 +474,18 @@ def test_UMAP(self): | |
| umap2 = self.test_matscholar.calculate_umap(n_neighbors=15, random_state=42) | ||
| assert (umap1 == umap2).all() | ||
|
|
||
| def test_PaCMAP(self): | ||
| """Test the PaCMAP function.""" | ||
| pacmap_params = {"random_state": 42} | ||
| assert isinstance(self.test_matscholar.calculate_pacmap(), np.ndarray) | ||
| assert self.test_matscholar.calculate_pacmap().shape == ( | ||
| len(self.test_matscholar.element_list), | ||
| 2, | ||
| ) | ||
| pacmap1 = self.test_matscholar.calculate_pacmap(**pacmap_params) | ||
| pacmap2 = self.test_matscholar.calculate_pacmap(**pacmap_params) | ||
| assert (pacmap1 == pacmap2).all() | ||
|
Comment on lines
+477
to
+487
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kept exact equality to match the existing |
||
|
|
||
|
|
||
| class SpeciesEmbeddingTest(unittest.TestCase): | ||
| """Test the SpeciesEmbedding class.""" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left off for consistency with the sibling reducers —
calculate_pca/calculate_tsne/calculate_umapare also unannotated. Glad to add-> np.ndarrayacross all four reducer methods if you'd like them annotated together.