Visualizing tools’ results¶
graph stuff:
https://holoviews.org/gallery/demos/bokeh/network_graph.html
https://holoviews.org/reference/elements/bokeh/Graph.html
%load_ext autoreload
%autoreload 2
import holoviews as hv
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
from anndata import AnnData
adata = sc.datasets.pbmc68k_reduced()
Embeddings¶
sc.pp.pca(adata)
sc.pp.neighbors(adata)
sc.tl.umap(adata)
/home/docs/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.
warnings.warn(msg)
scanpy.pl.embedding() (i.e. scanpy.pl.pca(), scanpy.pl.umap(), scanpy.pl.diffmap(), scanpy.pl.tsne(), scanpy.pl.draw_graph())
missing:
add_outlinelegend_positiondoesn’t work for graphs
missing convenience:
groupsto restrict to a subset easily
# without edges, see `scatter` in Basic notebook
hv_sc.draw_graph(adata, A.obsm["X_umap"], "distances", [A.obs["bulk_labels"]]).opts(
node_color=A.obs["bulk_labels"],
node_cmap="tab10",
aspect="square",
show_legend=True,
legend_position="right",
)
scanpy.pl.ranking() (scanpy.pl.pca_loadings(), scanpy.pl.pca_variance_ratio())
missing:
xoffset/yoffsettaken from dimension ortext_xoffset/text_yoffsetavailable forLabels: https://github.com/holoviz/holoviews/issues/3884
hv.Layout([
hv_sc.ranking(adata, A.varm["PCs"][0]).opts(aspect=1.2),
hv_sc.ranking(adata, A.varm["PCs"][0], include_lowest=False).opts(aspect=0.6),
]).opts(shared_axes=False)
scanpy.pl.pca_overview() is just a layout of multiple pca_ plots
TODO: maybe better approach: https://holoviews.org/gallery/demos/bokeh/iris_density_grid.html
sc.tl.embedding_density(adata, basis="umap", groupby="phase")
# sc.pl.embedding_density(adata, "umap", groupby="phase")
hv_sc.embedding_density(adata, A.obsm["X_umap"], groupby="phase")
Pseudotime and clustering¶
adata.uns["iroot"] = np.flatnonzero(adata.obs["bulk_labels"] == "CD56+ NK")[0]
sc.tl.diffmap(adata)
sc.tl.dpt(adata, n_branchings=1)
scanpy.pl.dpt_timeseries() (as_heatmap=True)
missing:
figure out why the weird extra vdim is needed
markers: list[str] = ["C1QA", "PSAP", "CD79A", "CD79B", "CST3", "LYZ"]
display(
hv.HeatMap(adata[:, markers], [A.obs.index, A.var.index], [A.X[:, :]])
* hv.VLines(adata.uns["dpt_changepoints"])
)
---------------------------------------------------------------------------
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:1491, in Dimensioned._repr_mimebundle_(self, include, exclude)
1484 def _repr_mimebundle_(self, include=None, exclude=None):
1485 """Resolves the class hierarchy for the class rendering the
1486 object using any display hooks registered on Store.display
1487 hooks. The output of all registered display_hooks is then
1488 combined and returned.
1489
1490 """
-> 1491 return Store.render(self)
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/core/options.py:1517, in Store.render(cls, obj)
1515 data, metadata = {}, {}
1516 for hook in hooks:
-> 1517 ret = hook(obj)
1518 if ret is None:
1519 continue
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:330, in pprint_display(obj)
328 if not ip.display_formatter.formatters["text/plain"].pprint:
329 return None
--> 330 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:298, in display(obj, raw_output, **kwargs)
296 elif isinstance(obj, (CompositeOverlay, ViewableElement)):
297 with option_state(obj):
--> 298 output = element_display(obj)
299 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
300 with option_state(obj):
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:193, in display_hook.<locals>.wrapped(element)
191 try:
192 max_frames = OutputSettings.options["max_frames"]
--> 193 mimebundle = fn(element, max_frames=max_frames)
194 if mimebundle is None:
195 return {}, {}
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:231, in element_display(element, max_frames)
228 if type(element) not in Store.registry[backend]:
229 return None
--> 231 return render(element)
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/ipython/display_hooks.py:62, in render(obj, **kwargs)
59 if renderer.fig == "pdf":
60 renderer = renderer.instance(fig="png")
---> 62 return renderer.components(obj, **kwargs)
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:450, in Renderer.components(self, obj, fmt, comm, **kwargs)
448 embed = not (dynamic or streams or self.widget_mode == "live") or config.embed
449 if embed or config.comms == "default":
--> 450 return self._render_panel(plot, embed, comm)
451 return self._render_ipywidget(plot)
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/renderer.py:457, in Renderer._render_panel(self, plot, embed, comm)
455 doc = Document()
456 with config.set(embed=embed):
--> 457 model = plot.layout._render_model(doc, comm)
458 if embed:
459 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:77, in BokehRenderer.get_plot(self_or_cls, obj, doc, renderer, **kwargs)
70 @bothmethod
71 def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs):
72 """Given a HoloViews Viewable return a corresponding plot instance.
73 Allows supplying a document attach the plot to, useful when
74 combining the bokeh model with another plot.
75
76 """
---> 77 plot = super().get_plot(obj, doc, renderer, **kwargs)
78 if plot.document is None:
79 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:291, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
287 defaults = [kd.default for kd in plot.dimensions]
288 init_key = tuple(
289 v if d is None else d for v, d in zip(plot.keys[0], defaults, strict=None)
290 )
--> 291 plot.update(init_key)
292 else:
293 plot = obj
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/plot.py:1069, in DimensionedPlot.update(self, key)
1067 def update(self, key):
1068 if len(self) == 1 and key in (0, self.keys[0]) and not self.drawn:
-> 1069 return self.initialize_plot()
1070 item = self.__getitem__(key)
1071 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:3882, in OverlayPlot.initialize_plot(self, ranges, plot, plots)
3880 if plot and not self.overlaid:
3881 self._update_plot(key, plot, element)
-> 3882 self._update_ranges(element, ranges)
3884 panels = []
3885 subcoord_y_glyph_renderers = []
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:1577, in ElementPlot._update_ranges(self, element, ranges)
1574 y_range = self.handles["y_range"]
1575 plot = self.handles["plot"]
-> 1577 self._update_main_ranges(element, x_range, y_range, ranges)
1579 if self._subcoord_overlaid:
1580 return
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:1629, in ElementPlot._update_main_ranges(self, element, x_range, y_range, ranges, subcoord)
1627 xfactors, yfactors = None, None
1628 if any(isinstance(ax_range, FactorRange) for ax_range in [x_range, y_range]):
-> 1629 xfactors, yfactors = self._get_factors(element, ranges)
1630 framewise = self.framewise
1631 streaming = self.streaming and any(
1632 stream._triggering and stream.following for stream in self.streaming
1633 )
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:3828, in OverlayPlot._get_factors(self, overlay, ranges)
3826 if el is not None:
3827 elranges = util.match_spec(el, ranges)
-> 3828 xfs, yfs = sp._get_factors(el, elranges)
3829 if len(xfs):
3830 xfactors.append(xfs)
File ~/.local/share/hatch/env/virtual/hv-anndata/STk7F69l/docs/lib/python3.13/site-packages/holoviews/plotting/bokeh/element.py:2048, in ElementPlot._get_factors(self, element, ranges)
2046 def _get_factors(self, element, ranges):
2047 """Get factors for categorical axes."""
-> 2048 xdim, ydim = element.dimensions()[:2]
2049 xvals = self._get_dimension_factors(element, ranges, xdim)
2050 yvals = self._get_dimension_factors(element, ranges, ydim)
ValueError: not enough values to unpack (expected 2, got 1)
:Overlay
.HeatMap.I :HeatMap [A.obs.index,A.var.index] (A.X[:, :])
.VLines.I :VLines [x]
markers: list[str] = ["C1QA", "PSAP", "CD79A", "CD79B", "CST3", "LYZ"]
hv.HeatMap(
adata[adata.obs["dpt_order_indices"], markers],
[A.obs.index, A.var.index],
[A.X[:, :]],
).opts(xticks=0, colorbar=True, width=400, height=200) * hv.VLines(
(adata.uns["dpt_changepoints"], [0] * len(adata.uns["dpt_changepoints"])),
vdims=["?"],
)
scanpy.pl.dpt_timeseries() (as_heatmap=False)
def dpt_timeseries(adata: AnnData):
adata = adata[adata.obs["dpt_order_indices"]].copy()
return (
hv.Overlay([
hv.Points(adata, [A.obs.index, A.X[:, gene]], label=gene)
for gene in adata.var_names
])
* hv.VLines(
(
adata.uns["dpt_changepoints"],
[0] * len(adata.uns["dpt_changepoints"]),
),
vdims=["?"],
)
).opts(xticks=0)
markers = ["C1QA", "PSAP", "CD79A", "CD79B", "CST3", "LYZ"]
dpt_timeseries(adata[:, markers]).opts(width=800, legend_position="right")
scanpy.pl.dpt_groups_pseudotime()
Skip for now, since it’s broken in scanpy: https://github.com/scverse/scanpy/issues/3086
try:
sc.pl.dpt_groups_pseudotime(adata)
except Exception: # ruff:ignore[blind-except]
import traceback
traceback.print_exc()
scanpy.pl.correlation_matrix()
missing:
again: dendrogram on heatmaps
invalid warning:
“WARNING:param.RasterPlot02026: aspect value was ignored because absolute width and height values were provided. Either supply explicit frame_width and frame_height to achieve desired aspect OR supply a combination of width or height and an aspect value.”
sc.tl.dendrogram(adata, "bulk_labels")
# sc.pl.correlation_matrix(adata, groupby="bulk_labels")
def correlation_matrix(adata: AnnData, groupby: str):
# from scipy.sparse import coo_array
dendrogram_key = f"dendrogram_{groupby}" # _get_dendrogram_key(...)
mat = adata.uns[dendrogram_key]["correlation_matrix"]
index = adata.uns[dendrogram_key]["categories_idx_ordered"]
mat = mat[index[::-1], :][:, index] # TODO: why do we have to flip? # ruff:ignore[missing-todo-link]
labels = list(adata.obs[groupby].cat.categories)
labels = np.array(labels).astype("str")[index]
ticks = list(enumerate(labels))
return hv.Image(
mat, bounds=(-0.5, -0.5, len(labels) - 0.5, len(labels) - 0.5)
).opts(
xticks=ticks,
yticks=ticks,
xrotation=45,
frame_width=400,
frame_height=400,
aspect=1,
)
correlation_matrix(adata, "bulk_labels")
WARNING:param.RasterPlot01191: aspect value was ignored because absolute width and height values were provided. Either supply explicit frame_width and frame_height to achieve desired aspect OR supply a combination of width or height and an aspect value.
Marker genes¶
scanpy.pl.rank_genes_groups(), scanpy.pl.rank_genes_groups_dotplot(), scanpy.pl.rank_genes_groups_heatmap(), scanpy.pl.rank_genes_groups_matrixplot(), scanpy.pl.rank_genes_groups_tracksplot(), scanpy.pl.rank_genes_groups_stacked_violin(), scanpy.pl.rank_genes_groups_violin()
are all just pre-parametrized versions of other plots; rank_genes_groups is ranking, the others are just the suffix.