diff --git a/pyproject.toml b/pyproject.toml index b699adceb4..12f21a2aa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ dependencies = [ "tqdm", "scikit-learn>=1.1,<1.6", "statsmodels>=0.13", - "patsy!=1.0.0", # https://github.com/pydata/patsy/issues/215 + "patsy!=1.0.0", # https://github.com/pydata/patsy/issues/215 "networkx>=2.8", "natsort", "joblib", @@ -65,8 +65,9 @@ dependencies = [ "pynndescent>=0.5", "packaging>=21.3", "session-info2", - "legacy-api-wrap>=1.4", # for positional API deprecations + "legacy-api-wrap>=1.4", # for positional API deprecations "typing-extensions; python_version < '3.13'", + "anndata-plot @ git+https://github.com/scverse/anndata-plot.git", ] dynamic = [ "version" ] @@ -139,6 +140,8 @@ dev = [ "pre-commit", # static checking "towncrier", # release note management ] + + # Algorithms paga = [ "igraph" ] louvain = [ "igraph", "louvain>=0.6.0,!=0.6.2" ] # Louvain community detection @@ -160,6 +163,8 @@ packages = [ "src/testing", "src/scanpy" ] source = "vcs" raw-options.version_scheme = "release-branch-semver" +[tool.hatch.metadata] +allow-direct-references = true [tool.pytest.ini_options] addopts = [ "--import-mode=importlib", diff --git a/src/scanpy/plotting/_anndata.py b/src/scanpy/plotting/_anndata.py index e7d290d05e..12906d2654 100755 --- a/src/scanpy/plotting/_anndata.py +++ b/src/scanpy/plotting/_anndata.py @@ -17,6 +17,7 @@ from packaging.version import Version from pandas.api.types import CategoricalDtype, is_numeric_dtype from scipy.sparse import issparse +import holoviews as hv from .. import get from .. import logging as logg @@ -759,9 +760,11 @@ def violin( xlabel: str = "", ylabel: str | Sequence[str] | None = None, rotation: float | None = None, + ncols: int = 1, show: bool | None = None, save: bool | str | None = None, ax: Axes | None = None, + interactive: bool = False, # deprecatd scale: DensityNorm | Empty = _empty, **kwds, @@ -868,7 +871,10 @@ def violin( pl.stacked_violin """ - import seaborn as sns # Slow import, only import if called + if not interactive: + import seaborn as sns # Slow import, only import if called + else: + import anndata_plot as adp sanitize_anndata(adata) use_raw = _check_use_raw(adata, use_raw) @@ -913,11 +919,46 @@ def violin( x = groupby ys = keys + if ax is None: + axs, _, _, _ = setup_axes( + ax, + panels=["x"] if groupby is None else keys, + show_ticks=True, + right_margin=0.3, + ) + else: + axs = [ax] + + if interactive: + if len(ys) > 1: + plots = [] + print(ys) + for y in ys: + plots.append(adp.pl.violin( + data=obs_df, + var_names=y, + groupby=groupby, + **kwds, + )) + else: + return adp.pl.violin( + data=obs_df, + var_names=ys[0], + groupby=groupby, + **kwds, + ) + + if ncols > 1: + grid = hv.Layout(plots).cols(ncols) + return grid + if multi_panel and groupby is None and len(ys) == 1: # This is a quick and dirty way for adapting scales across several # keys if groupby is None. y = ys[0] + + g: sns.axisgrid.FacetGrid = sns.catplot( y=y, data=obs_tidy, @@ -954,15 +995,6 @@ def violin( kwds.setdefault("cut", 0) kwds.setdefault("inner") - if ax is None: - axs, _, _, _ = setup_axes( - ax, - panels=["x"] if groupby is None else keys, - show_ticks=True, - right_margin=0.3, - ) - else: - axs = [ax] for ax, y, ylab in zip(axs, ys, ylabel): ax = sns.violinplot( x=x, diff --git a/src/scanpy/plotting/_stacked_violin.py b/src/scanpy/plotting/_stacked_violin.py index 0e883c07f4..923ad8d867 100644 --- a/src/scanpy/plotting/_stacked_violin.py +++ b/src/scanpy/plotting/_stacked_violin.py @@ -3,12 +3,18 @@ import warnings from typing import TYPE_CHECKING +import anndata_plot as adp +import holoviews as hv + +_ = adp # so linters don't complain import numpy as np import pandas as pd from matplotlib import pyplot as plt from matplotlib.colors import is_color_like from packaging.version import Version +import scanpy as sc + from .. import logging as logg from .._compat import old_positionals from .._settings import settings @@ -23,6 +29,9 @@ savefig_or_show, ) +hv.extension("bokeh") + + if TYPE_CHECKING: from collections.abc import Mapping, Sequence from typing import Literal, Self @@ -847,6 +856,19 @@ def stacked_violin( yticklabels=yticklabels, linewidth=kwds.get("linewidth", _empty), ).legend(title=colorbar_title) + + hv.extension("matplotlib") + + adata = sc.datasets.pbmc3k() + sc.pp.neighbors(adata) + sc.tl.umap(adata) + sc.tl.leiden(adata) + print(adata) + + return hv.Scatter(adata, "obsm.X_umap.0", ["obsm.X_umap.1", "obs.leiden"]).opts( + color="obs.leiden", cmap="Category20" + ) + if return_fig: return vp vp.make_figure()