Basic plotting tools

hv resources

  • Gallery: https://holoviews.org/reference/

  • https://holoviews.org/user_guide/Customizing_Plots.html

  • https://holoviews.org/user_guide/Style_Mapping.html

  • https://holoviews.org/user_guide/Colormaps.html

  • https://holoviews.org/user_guide/Plotting_with_Bokeh.html

%load_ext autoreload
%autoreload 2
import holoviews as hv

import hv_anndata
from hv_anndata import A, register
from hv_anndata import scanpy as hv_sc

register()

hv.extension("bokeh")
import numpy as np
import scanpy as sc

rng = np.random.default_rng()
adata = sc.datasets.pbmc68k_reduced()
adata.layers["counts"] = adata.raw.X
del adata.raw
sc.tl.umap(adata)
adata
AnnData object with n_obs × n_vars = 700 × 765
    obs: 'bulk_labels', 'n_genes', 'percent_mito', 'n_counts', 'S_score', 'G2M_score', 'phase', 'louvain'
    var: 'n_counts', 'means', 'dispersions', 'dispersions_norm', 'highly_variable'
    uns: 'bulk_labels_colors', 'louvain', 'louvain_colors', 'neighbors', 'pca', 'rank_genes_groups', 'umap'
    obsm: 'X_pca', 'X_umap'
    varm: 'PCs'
    obsp: 'connectivities', 'distances'
    layers: None, 'counts'

scanpy.pl.scatter()

missing features:

  • use_raw (deprecated!)

  • na_color (not super important)

  • color=dimension in plotly backend: holoviz/holoviews#5222

missing convenience:

  • basis for easy X&Y

(
    hv_sc.scatter(adata, A.X[:, ["PSAP", "C1QA"]], color=A.obs["bulk_labels"]).opts(
        cmap="tab10", show_legend=False
    )
    + hv_sc.scatter(
        adata, A.layers["counts"][:, ["PSAP", "C1QA"]], color=A.obs["bulk_labels"]
    ).opts(cmap="tab10")
)
# add NAs to check how missing values look
adata_scatter = adata.copy()
adata_scatter.obs.loc[
    (
        (adata_scatter.obs["bulk_labels"] == "Dendritic")
        & rng.choice([True, False], size=len(adata_scatter))
    ),
    "bulk_labels",
] = np.nan

hv_sc.umap(adata_scatter) + hv_sc.umap(adata_scatter, color=A.obs["bulk_labels"]).opts(
    cmap="tab10"
)

scanpy.pl.heatmap()

missing:

  • groupby / TickBar

  • standard_scale (see implemantation in hv_anndata.Dotmap)

  • dendrogram doesn’t work on heatmap (again ndim problem: dendrogram should tread (n, 1) as 1D)

markers = ["C1QA", "PSAP", "CD79A", "CD79B", "CST3", "LYZ"]
(
    hv_sc.heatmap(adata[:, markers], A.X, [A.obs["n_counts"]], add_dendrogram="obs")
    + hv_sc.heatmap(adata[:, markers], A.layers["counts"], [A.obs["n_counts"]])
).cols(1).opts(hv.opts.HeatMap(xticks=0, width=800, height=300)).opts(shared_axes=False)
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/operation/element.py:1365: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  if d not in self.p.adjoint_dims:
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/dimension.py:1033: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  dims = [d for d in all_dims if dimension == d]
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/operation/element.py:1406: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  if dim not in self.p.adjoint_dims:
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/data/xarray.py:183: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  data = {d: np.asarray(values) if d in kdims else values
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/data/xarray.py:230: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  if c in kdims or len(da[c].shape) != 1 or da[c].shape[0] <= 1:
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/data/xarray.py:387: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  if dim in dataset.kdims:
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/dimension.py:1073: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  return next(i for i, d in enumerate(dimensions) if d == dim)
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/data/xarray.py:387: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  if dim in dataset.kdims:
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/dimension.py:1073: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  return next(i for i, d in enumerate(dimensions) if d == dim)
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/data/grid.py:326: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  if d not in dataset.kdims+virtual_coords]

TODO: support sparse data

hv_sc.heatmap(adata[:40], A.obsp["distances"]).opts(tools=["hover"])

scanpy.pl.dotplot()

missing:

  • var_group_* (highlight groups of var_names by drawing brackets)

markers = ["C1QA", "PSAP", "CD79A", "CD79B", "CST3", "LYZ"]
dm = hv_anndata.Dotmap(adata=adata, marker_genes=markers, groupby="bulk_labels")
dm
WARNING:param.main: JSONEditor was not imported on instantiation and may not render in a notebook. Restart the notebook kernel and ensure you load it as part of the extension using:

pn.extension('jsoneditor')
hv.operation.dendrogram(
    dm.plot(), adjoint_dims=["cluster"], main_dim="mean_expression", invert=True
)

scanpy.pl.tracksplot()

missing:

hv_sc.tracksplot(
    adata,
    A.X[:, ["C1QA", "PSAP", "CD79A", "CD79B", "CST3", "LYZ"]],
    color=A.obs["bulk_labels"],
).opts(hv.opts.Curve(aspect=20))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/dimension.py:1381, in Dimensioned._repr_mimebundle_(self, include, exclude)
   1374 def _repr_mimebundle_(self, include=None, exclude=None):
   1375     """Resolves the class hierarchy for the class rendering the
   1376     object using any display hooks registered on Store.display
   1377     hooks.  The output of all registered display_hooks is then
   1378     combined and returned.
   1379 
   1380     """
-> 1381     return Store.render(self)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/options.py:1433, in Store.render(cls, obj)
   1431 data, metadata = {}, {}
   1432 for hook in hooks:
-> 1433     ret = hook(obj)
   1434     if ret is None:
   1435         continue

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:340, in pprint_display(obj)
    338 if not ip.display_formatter.formatters['text/plain'].pprint:
    339     return None
--> 340 return display(obj, raw_output=True)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:311, in display(obj, raw_output, **kwargs)
    309 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
    310     with option_state(obj):
--> 311         output = layout_display(obj)
    312 elif isinstance(obj, (HoloMap, DynamicMap)):
    313     with option_state(obj):

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:205, in display_hook.<locals>.wrapped(element)
    203 try:
    204     max_frames = OutputSettings.options['max_frames']
--> 205     mimebundle = fn(element, max_frames=max_frames)
    206     if mimebundle is None:
    207         return {}, {}

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:274, in layout_display(layout, max_frames)
    271     max_frame_warning(max_frames)
    272     return None
--> 274 return render(layout)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:76, in render(obj, **kwargs)
     73 if renderer.fig == 'pdf':
     74     renderer = renderer.instance(fig='png')
---> 76 return renderer.components(obj, **kwargs)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:396, in Renderer.components(self, obj, fmt, comm, **kwargs)
    394 embed = (not (dynamic or streams or self.widget_mode == 'live') or config.embed)
    395 if embed or config.comms == 'default':
--> 396     return self._render_panel(plot, embed, comm)
    397 return self._render_ipywidget(plot)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:403, in Renderer._render_panel(self, plot, embed, comm)
    401 doc = Document()
    402 with config.set(embed=embed):
--> 403     model = plot.layout._render_model(doc, comm)
    404 if embed:
    405     return render_model(model, comm)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/viewable.py:775, in Viewable._render_model(self, doc, comm)
    773 if comm is None:
    774     comm = state._comm_manager.get_server_comm()
--> 775 model = self.get_root(doc, comm)
    777 if self._design and self._design.theme.bokeh_theme:
    778     doc.theme = self._design.theme.bokeh_theme

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:332, in Panel.get_root(self, doc, comm, preprocess)
    328 def get_root(
    329     self, doc: Document | None = None, comm: Comm | None = None,
    330     preprocess: bool = True
    331 ) -> Model:
--> 332     root = super().get_root(doc, comm, preprocess)
    333     # ALERT: Find a better way to handle this
    334     if hasattr(root, 'styles') and 'overflow-x' in root.styles:

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/viewable.py:705, in Renderable.get_root(self, doc, comm, preprocess)
    703 wrapper = self._design._wrapper(self)
    704 if wrapper is self:
--> 705     root = self._get_model(doc, comm=comm)
    706     if preprocess:
    707         self._preprocess(root)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:316, in Panel._get_model(self, doc, root, parent, comm)
    314 root = root or model
    315 self._models[root.ref['id']] = (model, parent)
--> 316 objects, _ = self._get_objects(model, [], doc, root, comm)
    317 props = self._get_properties(doc)
    318 props[self._property_mapping['objects']] = objects

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:298, in Panel._get_objects(self, model, old_objects, doc, root, comm)
    296 else:
    297     try:
--> 298         child = pane._get_model(doc, root, model, comm)
    299     except RerenderError as e:
    300         if e.layout is not None and e.layout is not self:

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/pane/holoviews.py:494, in HoloViews._get_model(self, doc, root, parent, comm)
    492     plot = self.object
    493 else:
--> 494     plot = self._render(doc, comm, root)
    496 plot.pane = self
    497 backend = plot.renderer.backend

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/pane/holoviews.py:588, in HoloViews._render(self, doc, comm, root)
    585     if comm:
    586         kwargs['comm'] = comm
--> 588 return renderer.get_plot(self.object, **kwargs)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/renderer.py:70, in BokehRenderer.get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     63 @bothmethod
     64 def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs):
     65     """Given a HoloViews Viewable return a corresponding plot instance.
     66     Allows supplying a document attach the plot to, useful when
     67     combining the bokeh model with another plot.
     68 
     69     """
---> 70     plot = super().get_plot(obj, doc, renderer, **kwargs)
     71     if plot.document is None:
     72         plot.document = Document() if self_or_cls.notebook_context else curdoc()

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:234, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    232 if isinstance(obj, AdjointLayout):
    233     obj = Layout(obj)
--> 234 plot = self_or_cls.plotting_class(obj)(obj, renderer=renderer,
    235                                        **plot_opts)
    236 defaults = [kd.default for kd in plot.dimensions]
    237 init_key = tuple(v if d is None else d for v, d in
    238                  zip(plot.keys[0], defaults, strict=None))

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/plot.py:748, in LayoutPlot.__init__(self, layout, keys, **params)
    746 def __init__(self, layout, keys=None, **params):
    747     super().__init__(layout, keys=keys, **params)
--> 748     self.layout, self.subplots, self.paths = self._init_layout(layout)
    749     if self.top_level:
    750         self.traverse(lambda x: attach_streams(self, x.hmap, 2),
    751                       [GenericElementPlot])

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/plot.py:801, in LayoutPlot._init_layout(self, layout)
    799     layout_count += 1
    800 num = 0 if empty else layout_count
--> 801 subplots, adjoint_layout = self._create_subplots(
    802     view, positions, layout_dimensions, frame_ranges, num=num
    803 )
    805 # Generate the AdjointLayoutsPlot which will coordinate
    806 # plotting of AdjointLayouts in the larger grid
    807 plotopts = self.lookup_options(view, 'plot').options

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/plot.py:870, in LayoutPlot._create_subplots(self, layout, positions, layout_dimensions, ranges, num)
    868     continue
    869 num = num if len(self.coords) > 1 else 0
--> 870 subplot = plot_type(element, keys=self.keys,
    871                     dimensions=self.dimensions,
    872                     layout_dimensions=layout_dimensions,
    873                     ranges=ranges, subplot=True, root=self.root,
    874                     uniform=self.uniform, layout_num=num,
    875                     renderer=self.renderer,
    876                     **dict({'shared_axes': self.shared_axes},
    877                            **plotopts))
    878 subplots[pos] = subplot
    879 if isinstance(plot_type, type) and issubclass(plot_type, GenericCompositePlot):

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:3108, in OverlayPlot.__init__(self, overlay, **kwargs)
   3106 def __init__(self, overlay, **kwargs):
   3107     self._multi_y_propagation = self.lookup_options(overlay, 'plot').options.get('multi_y', False)
-> 3108     super().__init__(overlay, **kwargs)
   3109     self.callbacks, self.source_streams = self._construct_callbacks()
   3110     self._multi_y_propagation = False

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/plot.py:1755, in GenericOverlayPlot.__init__(self, overlay, ranges, batched, keys, group_counter, **params)
   1752 if 'projection' not in params:
   1753     params['projection'] = self._get_projection(overlay)
-> 1755 super().__init__(overlay, ranges=ranges, keys=keys,
   1756                  batched=batched, **params)
   1758 if ('multi_y' in self.param) and self.multi_y:
   1759     for s in self.streams:

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:364, in ElementPlot.__init__(self, element, plot, **params)
    362 self._subcoord_standalone_ = None
    363 self.current_ranges = None
--> 364 super().__init__(element, **params)
    365 self.handles = {} if plot is None else self.handles['plot']
    366 self.static = len(self.hmap) == 1 and len(self.keys) == len(self.hmap)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/plot.py:1264, in GenericElementPlot.__init__(self, element, keys, ranges, dimensions, batched, overlaid, cyclic_index, zorder, style, overlay_dims, stream_sources, streams, **params)
   1262     if p in self.param and p in self._deprecations and pval is not None:
   1263         self.param.warning(self._deprecations[p])
-> 1264 super().__init__(keys=keys, dimensions=dimensions,
   1265                  dynamic=dynamic, **applied_params)
   1266 self.batched = batched
   1267 self.streams = get_nested_streams(self.hmap) if streams is None else streams

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/plot.py:435, in DimensionedPlot.__init__(self, keys, dimensions, layout_dimensions, uniform, subplot, adjoined, layout_num, style, subplots, dynamic, **params)
    433 self.ranges = {}
    434 self._updated = False # Whether the plot should be marked as updated
--> 435 super().__init__(**params)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/plot.py:70, in Plot.__init__(self, renderer, root, **params)
     67 def __init__(self, renderer=None, root=None, **params):
     68     params = {k: v for k, v in params.items()
     69               if k in self.param}
---> 70     super().__init__(**params)
     71     self.renderer = renderer if renderer else Store.renderers[self.backend].instance()
     72     self._force = False

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/param/parameterized.py:5879, in Parameterized.__init__(self, **params)
   5877 if self.param.name.default == self.__class__.__name__:
   5878     self.param._generate_name()
-> 5879 refs, deps = self.param._setup_params(**params)
   5880 object_count += 1
   5882 self._param__private.initialized = True

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/param/parameterized.py:2316, in as_uninitialized.<locals>.override_initialization(self_, *args, **kw)
   2314 original_initialized = parameterized_instance._param__private.initialized
   2315 parameterized_instance._param__private.initialized = False
-> 2316 ret = fn(self_, *args, **kw)
   2317 parameterized_instance._param__private.initialized = original_initialized
   2318 return ret

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/param/parameterized.py:2646, in Parameters._setup_params(self_, **params)
   2634         if ref:
   2635             warnings.warn(
   2636                 f"Parameter {name!r} on {pobj.owner} is being given a valid parameter "
   2637                 f"reference {val} but is implicitly allow_refs=False. "
   (...)   2644                 stacklevel=_find_stack_level(),
   2645             )
-> 2646     setattr(self, name, val)
   2647     continue
   2649 # Resolve references

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/param/parameterized.py:679, in instance_descriptor.<locals>._f(self, obj, val)
    677         instance_param.__set__(obj, val)
    678         return
--> 679 return f(self, obj, val)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/param/parameterized.py:1992, in Parameter.__set__(self, obj, val)
   1989     if is_async or val is Undefined:
   1990         return
-> 1992 self._validate(val)
   1994 _old = NotImplemented
   1995 # obj can be None if __set__ is called for a Parameterized class

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/param/parameterized.py:2274, in String._validate(self, val)
   2273 def _validate(self, val: t.Any):
-> 2274     self._validate_value(val, self.allow_None)
   2275     self._validate_regex(val, self.regex)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/param/parameterized.py:2268, in String._validate_value(self, value, allow_None)
   2266     return
   2267 if not isinstance(value, str):
-> 2268     raise ValueError(
   2269         f'{_validate_error_prefix(self)} only takes a string value, '
   2270         f'not value of {type(value)}.'
   2271     )

ValueError: String parameter 'GenericElementPlot.ylabel' only takes a string value, not value of <class 'hv_anndata._ref.AdDim'>.
:NdLayout   [marker]
   :NdOverlay   [A.obs['bulk_labels']]
      :Curve   [A.obs.index]   (PSAP,bulk_labels)

scanpy.pl.violin()

missing:

  • density_norm

hv_sc.violin(adata, A.obs[["percent_mito", "n_counts", "n_genes"]]).opts(
    hv.opts.Violin(ylim=(0, None))
)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/dimension.py:1381, in Dimensioned._repr_mimebundle_(self, include, exclude)
   1374 def _repr_mimebundle_(self, include=None, exclude=None):
   1375     """Resolves the class hierarchy for the class rendering the
   1376     object using any display hooks registered on Store.display
   1377     hooks.  The output of all registered display_hooks is then
   1378     combined and returned.
   1379 
   1380     """
-> 1381     return Store.render(self)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/options.py:1433, in Store.render(cls, obj)
   1431 data, metadata = {}, {}
   1432 for hook in hooks:
-> 1433     ret = hook(obj)
   1434     if ret is None:
   1435         continue

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:340, in pprint_display(obj)
    338 if not ip.display_formatter.formatters['text/plain'].pprint:
    339     return None
--> 340 return display(obj, raw_output=True)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:311, in display(obj, raw_output, **kwargs)
    309 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
    310     with option_state(obj):
--> 311         output = layout_display(obj)
    312 elif isinstance(obj, (HoloMap, DynamicMap)):
    313     with option_state(obj):

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:205, in display_hook.<locals>.wrapped(element)
    203 try:
    204     max_frames = OutputSettings.options['max_frames']
--> 205     mimebundle = fn(element, max_frames=max_frames)
    206     if mimebundle is None:
    207         return {}, {}

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:274, in layout_display(layout, max_frames)
    271     max_frame_warning(max_frames)
    272     return None
--> 274 return render(layout)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:76, in render(obj, **kwargs)
     73 if renderer.fig == 'pdf':
     74     renderer = renderer.instance(fig='png')
---> 76 return renderer.components(obj, **kwargs)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:396, in Renderer.components(self, obj, fmt, comm, **kwargs)
    394 embed = (not (dynamic or streams or self.widget_mode == 'live') or config.embed)
    395 if embed or config.comms == 'default':
--> 396     return self._render_panel(plot, embed, comm)
    397 return self._render_ipywidget(plot)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:403, in Renderer._render_panel(self, plot, embed, comm)
    401 doc = Document()
    402 with config.set(embed=embed):
--> 403     model = plot.layout._render_model(doc, comm)
    404 if embed:
    405     return render_model(model, comm)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/viewable.py:775, in Viewable._render_model(self, doc, comm)
    773 if comm is None:
    774     comm = state._comm_manager.get_server_comm()
--> 775 model = self.get_root(doc, comm)
    777 if self._design and self._design.theme.bokeh_theme:
    778     doc.theme = self._design.theme.bokeh_theme

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:332, in Panel.get_root(self, doc, comm, preprocess)
    328 def get_root(
    329     self, doc: Document | None = None, comm: Comm | None = None,
    330     preprocess: bool = True
    331 ) -> Model:
--> 332     root = super().get_root(doc, comm, preprocess)
    333     # ALERT: Find a better way to handle this
    334     if hasattr(root, 'styles') and 'overflow-x' in root.styles:

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/viewable.py:705, in Renderable.get_root(self, doc, comm, preprocess)
    703 wrapper = self._design._wrapper(self)
    704 if wrapper is self:
--> 705     root = self._get_model(doc, comm=comm)
    706     if preprocess:
    707         self._preprocess(root)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:316, in Panel._get_model(self, doc, root, parent, comm)
    314 root = root or model
    315 self._models[root.ref['id']] = (model, parent)
--> 316 objects, _ = self._get_objects(model, [], doc, root, comm)
    317 props = self._get_properties(doc)
    318 props[self._property_mapping['objects']] = objects

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:298, in Panel._get_objects(self, model, old_objects, doc, root, comm)
    296 else:
    297     try:
--> 298         child = pane._get_model(doc, root, model, comm)
    299     except RerenderError as e:
    300         if e.layout is not None and e.layout is not self:

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/pane/holoviews.py:494, in HoloViews._get_model(self, doc, root, parent, comm)
    492     plot = self.object
    493 else:
--> 494     plot = self._render(doc, comm, root)
    496 plot.pane = self
    497 backend = plot.renderer.backend

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/pane/holoviews.py:588, in HoloViews._render(self, doc, comm, root)
    585     if comm:
    586         kwargs['comm'] = comm
--> 588 return renderer.get_plot(self.object, **kwargs)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/renderer.py:70, in BokehRenderer.get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     63 @bothmethod
     64 def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs):
     65     """Given a HoloViews Viewable return a corresponding plot instance.
     66     Allows supplying a document attach the plot to, useful when
     67     combining the bokeh model with another plot.
     68 
     69     """
---> 70     plot = super().get_plot(obj, doc, renderer, **kwargs)
     71     if plot.document is None:
     72         plot.document = Document() if self_or_cls.notebook_context else curdoc()

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:239, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    236     defaults = [kd.default for kd in plot.dimensions]
    237     init_key = tuple(v if d is None else d for v, d in
    238                      zip(plot.keys[0], defaults, strict=None))
--> 239     plot.update(init_key)
    240 else:
    241     plot = obj

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/plot.py:958, in DimensionedPlot.update(self, key)
    956 def update(self, key):
    957     if len(self) == 1 and key in (0, self.keys[0]) and not self.drawn:
--> 958         return self.initialize_plot()
    959     item = self.__getitem__(key)
    960     self.traverse(lambda x: setattr(x, '_updated', True))

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/plot.py:954, in LayoutPlot.initialize_plot(self, plots, ranges)
    951     continue
    953 shared_plots = list(passed_plots) if self.shared_axes else None
--> 954 subplots = subplot.initialize_plot(ranges=ranges, plots=shared_plots)
    955 nsubplots = len(subplots)
    957 modes = {sp.sizing_mode for sp in subplots
    958          if sp.sizing_mode not in (None, 'auto', 'fixed')}

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/plot.py:1103, in AdjointLayoutPlot.initialize_plot(self, ranges, plots)
   1101     else:
   1102         passed_plots = plots + adjoined_plots
-> 1103         adjoined_plots.append(subplot.initialize_plot(ranges=ranges, plots=passed_plots))
   1104 self.drawn = True
   1105 return adjoined_plots or [None]

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:2220, in ElementPlot.initialize_plot(self, ranges, plot, plots, source)
   2218 if self.autorange:
   2219     self._setup_autorange()
-> 2220 self._init_glyphs(plot, element, ranges, source)
   2221 if not self.overlaid:
   2222     self._update_plot(key, plot, style_element)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:2548, in CompositeElementPlot._init_glyphs(self, plot, element, ranges, source, data, mapping, style)
   2546 if None in (data, mapping):
   2547     style = self.style[self.cyclic_index]
-> 2548     data, mapping, style = self.get_data(element, ranges, style)
   2550 keys = glyph_order(dict(data, **mapping), self._draw_order)
   2552 source_cache = {}

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/stats.py:551, in ViolinPlot.get_data(self, element, ranges, style)
    549 if element.kdims:
    550     key = tuple(d.pprint_value(k) for d, k in zip(element.kdims, key, strict=None))
--> 551 kde, line, segs, bars, scatter = self._kde_data(
    552     element, g, key, split_dim, split_cats, **kwargs
    553 )
    554 for k, v in segs.items():
    555     seg_data[k] += v

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/stats.py:488, in ViolinPlot._kde_data(self, element, el, key, split_dim, split_cats, **kwargs)
    486 elif self.inner == 'box':
    487     xpos = (*key, 0)
--> 488     q1, q2, q3, upper, lower, _ = self._box_stats(values)
    489     segments['x'].append(xpos)
    490     segments['y0'].append(lower)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/stats.py:164, in BoxWhiskerPlot._box_stats(self, vals)
    162     q1, q2, q3 = 0, 0, 0
    163     upper, lower = 0, 0
--> 164 outliers = vals[(vals > upper) | (vals < lower)]
    166 if is_cupy:
    167     return (q1.item(), q2.item(), q3.item(), upper.item(),
    168             lower.item(), cupy.asnumpy(outliers))

TypeError: unsupported operand type(s) for |: 'NumpyExtensionArray' and 'NumpyExtensionArray'
:Layout
   .Violin.I   :Violin   (percent_mito)
   .Violin.II  :Violin   (n_counts)
   .Violin.III :Violin   (n_genes)
hv_sc.violin(adata, A.obs["S_score"], color=A.obs["bulk_labels"]).opts(
    width=500, xrotation=30
)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/dimension.py:1381, in Dimensioned._repr_mimebundle_(self, include, exclude)
   1374 def _repr_mimebundle_(self, include=None, exclude=None):
   1375     """Resolves the class hierarchy for the class rendering the
   1376     object using any display hooks registered on Store.display
   1377     hooks.  The output of all registered display_hooks is then
   1378     combined and returned.
   1379 
   1380     """
-> 1381     return Store.render(self)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/options.py:1433, in Store.render(cls, obj)
   1431 data, metadata = {}, {}
   1432 for hook in hooks:
-> 1433     ret = hook(obj)
   1434     if ret is None:
   1435         continue

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:340, in pprint_display(obj)
    338 if not ip.display_formatter.formatters['text/plain'].pprint:
    339     return None
--> 340 return display(obj, raw_output=True)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:308, in display(obj, raw_output, **kwargs)
    306 elif isinstance(obj, (CompositeOverlay, ViewableElement)):
    307     with option_state(obj):
--> 308         output = element_display(obj)
    309 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
    310     with option_state(obj):

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:205, in display_hook.<locals>.wrapped(element)
    203 try:
    204     max_frames = OutputSettings.options['max_frames']
--> 205     mimebundle = fn(element, max_frames=max_frames)
    206     if mimebundle is None:
    207         return {}, {}

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:246, in element_display(element, max_frames)
    243 if type(element) not in Store.registry[backend]:
    244     return None
--> 246 return render(element)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:76, in render(obj, **kwargs)
     73 if renderer.fig == 'pdf':
     74     renderer = renderer.instance(fig='png')
---> 76 return renderer.components(obj, **kwargs)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:396, in Renderer.components(self, obj, fmt, comm, **kwargs)
    394 embed = (not (dynamic or streams or self.widget_mode == 'live') or config.embed)
    395 if embed or config.comms == 'default':
--> 396     return self._render_panel(plot, embed, comm)
    397 return self._render_ipywidget(plot)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:403, in Renderer._render_panel(self, plot, embed, comm)
    401 doc = Document()
    402 with config.set(embed=embed):
--> 403     model = plot.layout._render_model(doc, comm)
    404 if embed:
    405     return render_model(model, comm)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/viewable.py:775, in Viewable._render_model(self, doc, comm)
    773 if comm is None:
    774     comm = state._comm_manager.get_server_comm()
--> 775 model = self.get_root(doc, comm)
    777 if self._design and self._design.theme.bokeh_theme:
    778     doc.theme = self._design.theme.bokeh_theme

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:332, in Panel.get_root(self, doc, comm, preprocess)
    328 def get_root(
    329     self, doc: Document | None = None, comm: Comm | None = None,
    330     preprocess: bool = True
    331 ) -> Model:
--> 332     root = super().get_root(doc, comm, preprocess)
    333     # ALERT: Find a better way to handle this
    334     if hasattr(root, 'styles') and 'overflow-x' in root.styles:

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/viewable.py:705, in Renderable.get_root(self, doc, comm, preprocess)
    703 wrapper = self._design._wrapper(self)
    704 if wrapper is self:
--> 705     root = self._get_model(doc, comm=comm)
    706     if preprocess:
    707         self._preprocess(root)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:316, in Panel._get_model(self, doc, root, parent, comm)
    314 root = root or model
    315 self._models[root.ref['id']] = (model, parent)
--> 316 objects, _ = self._get_objects(model, [], doc, root, comm)
    317 props = self._get_properties(doc)
    318 props[self._property_mapping['objects']] = objects

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/layout/base.py:298, in Panel._get_objects(self, model, old_objects, doc, root, comm)
    296 else:
    297     try:
--> 298         child = pane._get_model(doc, root, model, comm)
    299     except RerenderError as e:
    300         if e.layout is not None and e.layout is not self:

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/pane/holoviews.py:494, in HoloViews._get_model(self, doc, root, parent, comm)
    492     plot = self.object
    493 else:
--> 494     plot = self._render(doc, comm, root)
    496 plot.pane = self
    497 backend = plot.renderer.backend

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/panel/pane/holoviews.py:588, in HoloViews._render(self, doc, comm, root)
    585     if comm:
    586         kwargs['comm'] = comm
--> 588 return renderer.get_plot(self.object, **kwargs)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/renderer.py:70, in BokehRenderer.get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     63 @bothmethod
     64 def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs):
     65     """Given a HoloViews Viewable return a corresponding plot instance.
     66     Allows supplying a document attach the plot to, useful when
     67     combining the bokeh model with another plot.
     68 
     69     """
---> 70     plot = super().get_plot(obj, doc, renderer, **kwargs)
     71     if plot.document is None:
     72         plot.document = Document() if self_or_cls.notebook_context else curdoc()

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:239, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    236     defaults = [kd.default for kd in plot.dimensions]
    237     init_key = tuple(v if d is None else d for v, d in
    238                      zip(plot.keys[0], defaults, strict=None))
--> 239     plot.update(init_key)
    240 else:
    241     plot = obj

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/plot.py:958, in DimensionedPlot.update(self, key)
    956 def update(self, key):
    957     if len(self) == 1 and key in (0, self.keys[0]) and not self.drawn:
--> 958         return self.initialize_plot()
    959     item = self.__getitem__(key)
    960     self.traverse(lambda x: setattr(x, '_updated', True))

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:2220, in ElementPlot.initialize_plot(self, ranges, plot, plots, source)
   2218 if self.autorange:
   2219     self._setup_autorange()
-> 2220 self._init_glyphs(plot, element, ranges, source)
   2221 if not self.overlaid:
   2222     self._update_plot(key, plot, style_element)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:2548, in CompositeElementPlot._init_glyphs(self, plot, element, ranges, source, data, mapping, style)
   2546 if None in (data, mapping):
   2547     style = self.style[self.cyclic_index]
-> 2548     data, mapping, style = self.get_data(element, ranges, style)
   2550 keys = glyph_order(dict(data, **mapping), self._draw_order)
   2552 source_cache = {}

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/stats.py:551, in ViolinPlot.get_data(self, element, ranges, style)
    549 if element.kdims:
    550     key = tuple(d.pprint_value(k) for d, k in zip(element.kdims, key, strict=None))
--> 551 kde, line, segs, bars, scatter = self._kde_data(
    552     element, g, key, split_dim, split_cats, **kwargs
    553 )
    554 for k, v in segs.items():
    555     seg_data[k] += v

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/stats.py:488, in ViolinPlot._kde_data(self, element, el, key, split_dim, split_cats, **kwargs)
    486 elif self.inner == 'box':
    487     xpos = (*key, 0)
--> 488     q1, q2, q3, upper, lower, _ = self._box_stats(values)
    489     segments['x'].append(xpos)
    490     segments['y0'].append(lower)

File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/stats.py:164, in BoxWhiskerPlot._box_stats(self, vals)
    162     q1, q2, q3 = 0, 0, 0
    163     upper, lower = 0, 0
--> 164 outliers = vals[(vals > upper) | (vals < lower)]
    166 if is_cupy:
    167     return (q1.item(), q2.item(), q3.item(), upper.item(),
    168             lower.item(), cupy.asnumpy(outliers))

TypeError: unsupported operand type(s) for |: 'NumpyExtensionArray' and 'NumpyExtensionArray'
:Violin   [A.obs['bulk_labels']]   (S_score)

scanpy.pl.stacked_violin()

missing:

  • see tracksplot above

  • can’t do .hist() or .operations.dendrogram on GridSpace

  • slow!

markers = ["C1QA", "PSAP", "CD79A", "CD79B", "CST3", "LYZ"]
hv_sc.stacked_violin(adata[:, markers], A.var.index, A.obs["bulk_labels"]).opts(
    hv.opts.Violin(frame_height=50, frame_width=50)
)

scanpy.pl.matrixplot()

missing:

  • aspect="equal" breaks (bokeh tries to divide None/None)

  • hist doesn’t align properly

markers = ["C1QA", "PSAP", "CD79A", "CD79B", "CST3", "LYZ"]
hv_sc.matrixplot(
    adata[:, markers], A.obs["bulk_labels"], data=A.layers["counts"], add_totals=True
).opts(hv.opts.Bars(width=150))

scanpy.pl.clustermap()

missing:

  • TickBar (see above)

hv_anndata.ClusterMap(adata=adata)

scanpy.pl.dendrogram(): holoviews.operation.dendrogram