{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "# Basic plotting tools\n", "\n", "hv resources\n", "- Gallery: https://holoviews.org/reference/\n", "- https://holoviews.org/user_guide/Customizing_Plots.html\n", "- https://holoviews.org/user_guide/Style_Mapping.html\n", "- https://holoviews.org/user_guide/Colormaps.html\n", "- https://holoviews.org/user_guide/Plotting_with_Bokeh.html" ] }, { "cell_type": "code", "execution_count": null, "id": "1", "metadata": {}, "outputs": [], "source": [ "%load_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "code", "execution_count": null, "id": "2", "metadata": {}, "outputs": [], "source": [ "import holoviews as hv\n", "\n", "import hv_anndata\n", "from hv_anndata import A, register\n", "from hv_anndata import scanpy as hv_sc\n", "\n", "register()\n", "\n", "hv.extension(\"bokeh\")" ] }, { "cell_type": "code", "execution_count": null, "id": "3", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scanpy as sc\n", "\n", "rng = np.random.default_rng()" ] }, { "cell_type": "code", "execution_count": null, "id": "4", "metadata": {}, "outputs": [], "source": [ "adata = sc.datasets.pbmc68k_reduced()\n", "adata.layers[\"counts\"] = adata.raw.X\n", "del adata.raw\n", "sc.tl.umap(adata)\n", "adata" ] }, { "cell_type": "markdown", "id": "5", "metadata": {}, "source": [ "{func}`scanpy.pl.scatter`\n", "\n", "missing features:\n", "- `use_raw` (deprecated!)\n", "- `na_color` (not super important)\n", "- `color=dimension` in plotly backend: {issue}`holoviz/holoviews#5222`\n", "\n", "missing convenience:\n", "- `basis` for easy X&Y" ] }, { "cell_type": "code", "execution_count": null, "id": "6", "metadata": {}, "outputs": [], "source": [ "(\n", " hv_sc.scatter(adata, A.X[:, [\"PSAP\", \"C1QA\"]], color=A.obs[\"bulk_labels\"]).opts(\n", " cmap=\"tab10\", show_legend=False\n", " )\n", " + hv_sc.scatter(\n", " adata, A.layers[\"counts\"][:, [\"PSAP\", \"C1QA\"]], color=A.obs[\"bulk_labels\"]\n", " ).opts(cmap=\"tab10\")\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "7", "metadata": {}, "outputs": [], "source": [ "# add NAs to check how missing values look\n", "adata_scatter = adata.copy()\n", "adata_scatter.obs.loc[\n", " (\n", " (adata_scatter.obs[\"bulk_labels\"] == \"Dendritic\")\n", " & rng.choice([True, False], size=len(adata_scatter))\n", " ),\n", " \"bulk_labels\",\n", "] = np.nan\n", "\n", "hv_sc.umap(adata_scatter) + hv_sc.umap(adata_scatter, color=A.obs[\"bulk_labels\"]).opts(\n", " cmap=\"tab10\"\n", ")" ] }, { "cell_type": "markdown", "id": "8", "metadata": {}, "source": [ "{func}`scanpy.pl.heatmap`\n", "\n", "missing:\n", "- `groupby` / [TickBar](https://github.com/holoviz/holoviews/issues/6658)\n", "- `standard_scale` (see implemantation in {class}`hv_anndata.Dotmap`)\n", " - maybe done using [`dim` expressions](https://holoviews.org/user_guide/Transforming_Elements.html)?\n", "- dendrogram doesn’t work on heatmap (again ndim problem: dendrogram should tread `(n, 1)` as 1D)" ] }, { "cell_type": "code", "execution_count": null, "id": "9", "metadata": {}, "outputs": [], "source": [ "markers = [\"C1QA\", \"PSAP\", \"CD79A\", \"CD79B\", \"CST3\", \"LYZ\"]\n", "(\n", " hv_sc.heatmap(adata[:, markers], A.X, [A.obs[\"n_counts\"]], add_dendrogram=\"obs\")\n", " + hv_sc.heatmap(adata[:, markers], A.layers[\"counts\"], [A.obs[\"n_counts\"]])\n", ").cols(1).opts(hv.opts.HeatMap(xticks=0, width=800, height=300)).opts(shared_axes=False)" ] }, { "cell_type": "markdown", "id": "10", "metadata": {}, "source": [ "TODO: support sparse data" ] }, { "cell_type": "code", "execution_count": null, "id": "11", "metadata": {}, "outputs": [], "source": [ "hv_sc.heatmap(adata[:40], A.obsp[\"distances\"]).opts(tools=[\"hover\"])" ] }, { "cell_type": "markdown", "id": "12", "metadata": {}, "source": [ "{func}`scanpy.pl.dotplot`\n", "\n", "missing:\n", "- `var_group_*` (highlight groups of `var_names` by drawing brackets)" ] }, { "cell_type": "code", "execution_count": null, "id": "13", "metadata": {}, "outputs": [], "source": [ "markers = [\"C1QA\", \"PSAP\", \"CD79A\", \"CD79B\", \"CST3\", \"LYZ\"]\n", "dm = hv_anndata.Dotmap(adata=adata, marker_genes=markers, groupby=\"bulk_labels\")\n", "dm" ] }, { "cell_type": "code", "execution_count": null, "id": "14", "metadata": {}, "outputs": [], "source": [ "hv.operation.dendrogram(\n", " dm.plot(), adjoint_dims=[\"cluster\"], main_dim=\"mean_expression\", invert=True\n", ")" ] }, { "cell_type": "markdown", "id": "15", "metadata": {}, "source": [ "{func}`scanpy.pl.tracksplot`\n", "\n", "missing:\n", "- `shared_xaxis=False` doesn’t seem to work on GridSpace\n", "- GridSpace can only be 2D or row-only, not col-only\n", "- [GridSpace `show_legend` broken](https://github.com/holoviz/holoviews/issues/5438)" ] }, { "cell_type": "code", "execution_count": null, "id": "16", "metadata": {}, "outputs": [], "source": [ "hv_sc.tracksplot(\n", " adata,\n", " A.X[:, [\"C1QA\", \"PSAP\", \"CD79A\", \"CD79B\", \"CST3\", \"LYZ\"]],\n", " color=A.obs[\"bulk_labels\"],\n", ").opts(hv.opts.Curve(aspect=20))" ] }, { "cell_type": "markdown", "id": "17", "metadata": {}, "source": [ "{func}`scanpy.pl.violin`\n", "\n", "missing:\n", "- `density_norm`" ] }, { "cell_type": "code", "execution_count": null, "id": "18", "metadata": {}, "outputs": [], "source": [ "hv_sc.violin(adata, A.obs[[\"percent_mito\", \"n_counts\", \"n_genes\"]]).opts(\n", " hv.opts.Violin(ylim=(0, None))\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "19", "metadata": {}, "outputs": [], "source": [ "hv_sc.violin(adata, A.obs[\"S_score\"], color=A.obs[\"bulk_labels\"]).opts(\n", " width=500, xrotation=30\n", ")" ] }, { "cell_type": "markdown", "id": "20", "metadata": {}, "source": [ "{func}`scanpy.pl.stacked_violin`\n", "\n", "missing:\n", "- see tracksplot above\n", "- can’t do `.hist()` or `.operations.dendrogram` on GridSpace\n", "- slow!" ] }, { "cell_type": "code", "execution_count": null, "id": "21", "metadata": {}, "outputs": [], "source": [ "markers = [\"C1QA\", \"PSAP\", \"CD79A\", \"CD79B\", \"CST3\", \"LYZ\"]\n", "hv_sc.stacked_violin(adata[:, markers], A.var.index, A.obs[\"bulk_labels\"]).opts(\n", " hv.opts.Violin(frame_height=50, frame_width=50)\n", ")" ] }, { "cell_type": "markdown", "id": "22", "metadata": {}, "source": [ "{func}`scanpy.pl.matrixplot`\n", "\n", "missing:\n", "- `aspect=\"equal\"` breaks (bokeh tries to divide `None/None`)\n", "- hist doesn’t align properly" ] }, { "cell_type": "code", "execution_count": null, "id": "23", "metadata": {}, "outputs": [], "source": [ "markers = [\"C1QA\", \"PSAP\", \"CD79A\", \"CD79B\", \"CST3\", \"LYZ\"]\n", "hv_sc.matrixplot(\n", " adata[:, markers], A.obs[\"bulk_labels\"], data=A.layers[\"counts\"], add_totals=True\n", ").opts(hv.opts.Bars(width=150))" ] }, { "cell_type": "markdown", "id": "24", "metadata": {}, "source": [ "{func}`scanpy.pl.clustermap`\n", "\n", "missing:\n", "- TickBar (see above)" ] }, { "cell_type": "code", "execution_count": null, "id": "25", "metadata": {}, "outputs": [], "source": [ "hv_anndata.ClusterMap(adata=adata)" ] }, { "cell_type": "markdown", "id": "26", "metadata": {}, "source": [ "{func}`scanpy.pl.dendrogram`: {class}`holoviews.operation.dendrogram`" ] } ], "metadata": { "kernelspec": { "display_name": "hv-anndata", "language": "python", "name": "hv-anndata" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.7" } }, "nbformat": 4, "nbformat_minor": 5 }