AnnData

import holoviews as hv

from hv_anndata import A
from hv_anndata.interface import register

register()

hv.extension("bokeh")

Create synthetic data

from string import ascii_lowercase

import numpy as np
import pandas as pd
import scipy.sparse as sp
from anndata import AnnData

gen = np.random.default_rng()
x = gen.random((100, 50), dtype=np.float32)
layers = dict(a=sp.random(100, 50, format="csr"))
obs = pd.DataFrame(
    dict(type=gen.integers(0, 3, size=100)),
    index=pd.Series(range(100)).astype(str).apply(lambda v: "cell-" + v),
)
var_grp = pd.Categorical(
    gen.integers(0, 6, size=50), categories=list(ascii_lowercase[:5])
)
var = pd.DataFrame(
    dict(grp=var_grp),
    index=pd.Series(range(50)).astype(str).apply(lambda v: "gene-" + v),
)
obsm = dict(umap=gen.random((100, 2)))
varp = dict(cons=sp.csr_array(sp.random(50, 50)))
adata = AnnData(x, obs, var, layers=layers, obsm=obsm, varm={}, obsp={}, varp=varp)
/tmp/ipykernel_1993/664159585.py:15: Pandas4Warning: Constructing a Categorical with a dtype and values containing non-null entries not in that dtype's categories is deprecated and will raise in a future version.
  var_grp = pd.Categorical(

Tabular mode

We can select the data to display using the Accessor object A. Here it is important that the data must be indexed along either the obs or the var dimension.

Let us visualize the UMAP data to start:

hv.Scatter(adata, A.obsm["umap"][0], [A.obsm["umap"][1], A.obs["type"]]).opts(
    color=A.obs["type"]
)

To make it easier to access data we can also specify the dimensions as strings:

scatter = hv.Scatter(adata, "obsm.umap.0", ["obsm.umap.1", "obs.type"]).opts(
    color="A.obs['type']"
)

scatter
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/element.py:98: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  return dimension in self.dimensions()

For an obs indexed dataset you can use the select method along the obs variables, e.g. we can select a specific type:

scatter.select({A.obs["type"]: 2})  # .select(dim(A.var["grp"]) == "a")
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/element.py:98: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  return dimension in self.dimensions()

We can also use the .iloc method to index along the obs dimension:

scatter.iloc[:50]
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/element.py:98: FutureWarning: Probably comparing to a dimension created from `Dimension.name`. This will not be supported in the future, please report as an issue. 
  return dimension in self.dimensions()

We cannot can mix and match obs and var dimensions:

from holoviews.core.data.interface import DataError

try:
    hv.Scatter(adata, A.obsm["umap"][0], A.var.index)
except DataError as e:
    print(e)
AnnData Dataset in tabular mode must reference data along either the obs or the var axis, not both.

Gridded Data

AnnData can also hold gridded data and we can render that.

When rendering an Element that assumes continuous coordinates (e.g. Image) it will render the integer indices:

img = hv.Image(adata, [A.var.index, A.obs.index], [A.X[:, :]])
img
/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)

When rendering into an element that assumes discrete values (e.g. HeatMap) the axes will be labelled:

hm = hv.HeatMap(adata, [A.obs.index, A.var.index], [A.X[:, :]])

hm.opts(responsive=True, height=800, xrotation=90)