Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
4bd98b6
Add docstrings
keller-mark Mar 22, 2024
b55fa95
More tests
keller-mark Mar 22, 2024
e1a0797
Fixes for tests
keller-mark Mar 22, 2024
f140bda
Typo
keller-mark Mar 22, 2024
745e79e
Linting
keller-mark Mar 22, 2024
c63e745
Ruff
keller-mark Mar 22, 2024
66cf7fd
More linting
keller-mark Mar 22, 2024
d705887
More linting
keller-mark Mar 22, 2024
af32d85
Merge branch 'main' into keller-mark/densmap-2
keller-mark May 16, 2024
ec2b6d9
Update references.bib
keller-mark May 16, 2024
b41ddbd
Update references.bib
keller-mark May 16, 2024
86a0df0
Merge
keller-mark Aug 8, 2024
335d5a9
Merge branch 'keller-mark/densmap-2' of github.com:keller-mark/scanpy…
keller-mark Aug 8, 2024
6ee89e7
Fix bug
keller-mark Aug 8, 2024
603afb3
Update
keller-mark Aug 8, 2024
a2bbcf8
Merge branch 'main' of github.com:keller-mark/scanpy into keller-mark…
keller-mark Dec 10, 2024
4d15620
Relese note
keller-mark Dec 10, 2024
e8350d2
Formatting
keller-mark Dec 10, 2024
9030cb4
Try pre-commit
keller-mark Dec 10, 2024
967da92
Fix random_State type
keller-mark Dec 10, 2024
b70ec7e
Refactor type
keller-mark Dec 11, 2024
1784fa4
Fix citation format
keller-mark Dec 11, 2024
e92785e
Fix bibtex key
keller-mark Dec 11, 2024
2ce47d6
Use partial to implement sc.tl.densmap
keller-mark Dec 17, 2024
5838162
Update params in test
keller-mark Dec 17, 2024
ba2e887
Revert convenience sc.tl.densmap
keller-mark Dec 17, 2024
dffadfe
Add image plotting test
keller-mark Dec 17, 2024
44cfafc
Fix variable naming in test
keller-mark Dec 17, 2024
0507434
Merge branch 'main' of github.com:scverse/scanpy into keller-mark/den…
keller-mark Jan 3, 2025
e4b81f5
Frameon
keller-mark Jan 4, 2025
0ec47b3
Revert frameon change
keller-mark Jan 5, 2025
4f3ae06
Merge branch 'main' into keller-mark/densmap-2
keller-mark Jan 11, 2025
faaf5ae
Merge branch 'main' of github.com:scverse/scanpy into keller-mark/den…
keller-mark Jan 14, 2025
4152565
Change type name
keller-mark Jan 16, 2025
3d73310
Merge
keller-mark Jan 16, 2025
8f8787a
Check if specific to densmap
keller-mark Jan 16, 2025
3b68fe2
Add expected image
keller-mark Jan 16, 2025
8d45d49
Skipif for numba
keller-mark Jan 22, 2025
3cc91fc
Use pkg_version
keller-mark Jan 22, 2025
e8e00e6
Update .azure-pipelines.yml
ilan-gold Jan 23, 2025
5df86ac
Update .azure-pipelines.yml
ilan-gold Jan 23, 2025
5c2be1e
Update .azure-pipelines.yml
ilan-gold Jan 23, 2025
1e4ac26
Update .azure-pipelines.yml
ilan-gold Jan 23, 2025
0f20962
Merge branch 'main' into keller-mark/densmap-2
keller-mark Feb 16, 2025
e40d9f7
Add numerical test for densmap. Add test for raises ValueError
keller-mark Feb 16, 2025
7a8b637
Merge with main
keller-mark Feb 16, 2025
a8f7bb4
Remove unused image fixtures
keller-mark Feb 16, 2025
99390f9
Update tests/test_embedding.py
keller-mark Feb 21, 2025
3777672
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 21, 2025
b262bc2
WIP: try adding types
keller-mark Feb 21, 2025
f8f0fb6
Merge branch 'keller-mark/densmap-2' of github.com:keller-mark/scanpy…
keller-mark Feb 21, 2025
f921dcc
Merge branch 'main' into keller-mark/densmap-2
keller-mark Feb 21, 2025
019035d
Merge branch 'main' into keller-mark/densmap-2
keller-mark Feb 21, 2025
b0df27e
Merge branch 'main' into keller-mark/densmap-2
keller-mark Oct 9, 2025
4967a00
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 9, 2025
22fb7a7
Fix import statement for _LegacyRandom
keller-mark Oct 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/scanpy/tools/_umap.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ def umap(
random_state=random_state,
)
X_umap = umap.fit_transform(X_contiguous)
else:
message = f"umap method parameter invalid: {method} not supported."
raise ValueError(message)

adata.obsm[key_obsm] = X_umap # annotate samples with UMAP coordinates
logg.info(
Expand Down
53 changes: 53 additions & 0 deletions tests/test_embedding.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import numpy as np
import pytest
from numpy.testing import assert_array_almost_equal, assert_array_equal, assert_raises
from sklearn.mixture import GaussianMixture

import scanpy as sc
from testing.scanpy._helpers.data import pbmc68k_reduced
Expand Down Expand Up @@ -109,3 +110,55 @@ def test_densmap():
sc.tl.umap(pbmc, method="densmap", method_kwds=dict(dens_lambda=2.3456))
d4 = pbmc.obsm["X_densmap"].copy()
assert_raises(AssertionError, assert_array_equal, d1, d4)


def test_umap_raises_for_unsupported_method():
pbmc = pbmc68k_reduced()

# Checking that umap function raises a ValueError
# if a user passes an invalid `method` parameter.
with assert_raises(ValueError):
sc.tl.umap(pbmc, method="method_does_not_exist")


def get_mean_ellipse_area(gm):
# Adapted from GMM covariances ellipse plotting tutorial.
# Reference: https://scikit-learn.org/stable/auto_examples/mixture/plot_gmm_covariances.html
result = []
for i in range(gm.n_components):
covariances = gm.covariances_[i][:2, :2]
v, _ = np.linalg.eigh(covariances)
v = 2.0 * np.sqrt(2.0) * np.sqrt(v)
width = v[0]
height = v[1]
result.append(np.pi * width * height)
return np.mean(np.array(result))


def test_densmap_differs_from_umap():
pbmc = pbmc68k_reduced()

# Check that the areas of ellipses that result from
# fitting a Gaussian mixture model to the results
# of UMAP and DensMAP are different,
# with DensMAP ellipses having a larger area on average.
random_state = 1234
sc.tl.umap(pbmc, method="densmap", random_state=random_state)
X_densmap = pbmc.obsm["X_densmap"].copy()
sc.tl.umap(pbmc, method="umap", random_state=random_state)
X_umap = pbmc.obsm["X_umap"].copy()

# We fit a mixture model with as many components as
# there are louvain clusters, in this case 11.
n_components = pbmc.obs["louvain"].unique().shape[0]
assert n_components == 11

gm_umap = GaussianMixture(n_components=n_components, random_state=random_state).fit(
X_umap
)
gm_densmap = GaussianMixture(
n_components=n_components, random_state=random_state
).fit(X_densmap)
mean_area_umap = get_mean_ellipse_area(gm_umap)
mean_area_densmap = get_mean_ellipse_area(gm_densmap)
assert mean_area_densmap > mean_area_umap
69 changes: 7 additions & 62 deletions tests/test_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@
if TYPE_CHECKING:
from collections.abc import Callable

from matplotlib.axes import Axes


HERE: Path = Path(__file__).parent
ROOT = HERE / "_images"

Expand Down Expand Up @@ -845,30 +842,9 @@ def test_rank_genes_groups(image_comparer, name, fn):

with plt.rc_context({"axes.grid": True, "figure.figsize": (4, 4)}):
fn(pbmc)
key = "ranked_genes" if name == "basic" else f"ranked_genes_{name}"
save_and_compare_images(key)
plt.close()


def test_rank_genes_group_axes(image_comparer):
fn = next(fn for name, fn in _RANK_GENES_GROUPS_PARAMS if name == "basic")

save_and_compare_images = partial(image_comparer, ROOT, tol=23)

pbmc = pbmc68k_reduced()
sc.tl.rank_genes_groups(pbmc, "louvain", n_genes=pbmc.raw.shape[1])

pbmc.var["symbol"] = pbmc.var.index + "__"

fig, ax = plt.subplots(figsize=(12, 16))
ax.set_axis_off()
with plt.rc_context({"axes.grid": True}):
axes: list[Axes] = fn(pbmc, ax=ax, show=False)

assert len(axes) == 11
fig.show()
save_and_compare_images("ranked_genes")
plt.close()
key = "ranked_genes" if name == "basic" else f"ranked_genes_{name}"
save_and_compare_images(key)
plt.close()


@pytest.fixture(scope="session")
Expand Down Expand Up @@ -1042,11 +1018,9 @@ def pbmc_scatterplots_session() -> AnnData:
pbmc.layers["sparse"] = pbmc.raw.X / 2
pbmc.layers["test"] = pbmc.X.copy() + 100
pbmc.var["numbers"] = [str(x) for x in range(pbmc.shape[1])]
sc.pp.neighbors(pbmc, random_state=np.random.RandomState(1))
sc.pp.neighbors(pbmc)
sc.tl.tsne(pbmc, random_state=0, n_pcs=30)
sc.tl.diffmap(pbmc)
sc.tl.umap(pbmc, key_added="X_another_umap", random_state=np.random.RandomState(1))
sc.tl.umap(pbmc, method="densmap", random_state=np.random.RandomState(1))
return pbmc


Expand Down Expand Up @@ -1206,36 +1180,6 @@ def test_scatterplots(image_comparer, pbmc_scatterplots, id, fn):
save_and_compare_images(id)


@pytest.mark.skipif(
pkg_version("numba") < Version("0.61.0"),
reason="Same random_state value produces different UMAP results between numba versions. See #2946",
)
@pytest.mark.parametrize(
("id", "fn"),
[
(
"another_umap",
partial(
sc.pl.embedding,
basis="X_another_umap",
),
),
(
"densmap_nocolor",
partial(
sc.pl.embedding,
basis="X_densmap",
),
),
],
)
def test_umap_scatterplots(image_comparer, pbmc_scatterplots, id, fn):
save_and_compare_images = partial(image_comparer, ROOT, tol=15)

fn(pbmc_scatterplots, show=False)
save_and_compare_images(id)


def test_scatter_embedding_groups_and_size(image_comparer):
# test that the 'groups' parameter sorts
# cells, such that the cells belonging to the groups are
Expand Down Expand Up @@ -1512,10 +1456,11 @@ def test_rankings(image_comparer):


# TODO: Make more generic
def test_scatter_rep(tmp_path):
def test_scatter_rep(tmpdir):
"""
Test to make sure I can predict when scatter reps should be the same
"""
TESTDIR = Path(tmpdir)
rep_args = {
"raw": {"use_raw": True},
"layer": {"layer": "layer", "use_raw": False},
Expand All @@ -1530,7 +1475,7 @@ def test_scatter_rep(tmp_path):
columns=["rep", "gene", "result"],
)
states["outpth"] = [
tmp_path / f"{state.gene}_{state.rep}_{state.result}.png"
TESTDIR / f"{state.gene}_{state.rep}_{state.result}.png"
for state in states.itertuples()
]
pattern = np.array(list(chain.from_iterable(repeat(i, 5) for i in range(3))))
Expand Down