|
7 | 7 | }, |
8 | 8 | "source": [ |
9 | 9 | "# Session 3: Available View Types\n", |
10 | | - "This notebook demonstrates additional view types that are available in Vitessce, including both visualization and control views.\n" |
| 10 | + "\n", |
| 11 | + "Vitessce ships with a large number of interactive visualization and control views. In this notebook, we look at some types of views that you may not be familiar with.\n", |
| 12 | + "\n", |
| 13 | + "\n", |
| 14 | + "The views available in Vitessce can generally be categorized into:\n", |
| 15 | + "- Spatial view (covered in notebook 01)\n", |
| 16 | + "- Scatterplot view for dimensionality reductions / embeddings\n", |
| 17 | + "- Heatmap for expression data\n", |
| 18 | + "- Genomic profiles for quantitative features aggregated by cell type\n", |
| 19 | + "- Control views\n", |
| 20 | + " - Cell set manager\n", |
| 21 | + " - Feature list\n", |
| 22 | + " - Spatial layer controller\n", |
| 23 | + "- Statistical plots\n", |
| 24 | + " - Cell set sizes bar plot\n", |
| 25 | + " - Expression distribution per cell set violin plots\n", |
| 26 | + " - Expression distribution histogram\n", |
| 27 | + " - Cell set and sample set sizes treemap\n", |
| 28 | + " - Volcano plot\n", |
| 29 | + " - Cell type composition bar plot\n", |
| 30 | + " - Gene set enrichment bar plot\n", |
| 31 | + " - Gating scatterplot\n", |
| 32 | + "\n", |
| 33 | + "We cover:\n", |
| 34 | + "\n", |
| 35 | + "1. **Embedding views** — UMAP, PCA, t-SNE scatterplots\n", |
| 36 | + "2. **Expression views** — heatmap, dot plot, violin/distribution plot\n", |
| 37 | + "3. **Spatial views** — tissue image, layer controller\n", |
| 38 | + "4. **Control views** — cell-set tree, gene list, status bar\n", |
| 39 | + "\n", |
| 40 | + "We use the EasyVitessce Scanpy API for most examples because it is the most concise, then show how to access additional view types through the lower-level `VitessceConfig` API." |
11 | 41 | ] |
12 | 42 | }, |
13 | 43 | { |
|
17 | 47 | "id": "8i7E1aKwtT8c" |
18 | 48 | }, |
19 | 49 | "outputs": [], |
20 | | - "source": [] |
| 50 | + "source": [ |
| 51 | + "!pip install \"vitessce[all]==3.9.2\" \"scanpy\" \"easy-vitessce==0.0.12\" \"spatialdata==0.7.3\" \"spatialdata-plot==0.4.0\"" |
| 52 | + ] |
21 | 53 | }, |
22 | 54 | { |
23 | 55 | "cell_type": "code", |
|
26 | 58 | "id": "gli0oO6yszUm" |
27 | 59 | }, |
28 | 60 | "outputs": [], |
29 | | - "source": [] |
| 61 | + "source": [ |
| 62 | + "import easy_vitessce as ev\n", |
| 63 | + "import scanpy as sc\n", |
| 64 | + "\n", |
| 65 | + "# Load the reduced PBMC dataset (ships with Scanpy, no download required)\n", |
| 66 | + "adata = sc.datasets.pbmc68k_reduced()\n", |
| 67 | + "adata" |
| 68 | + ] |
| 69 | + }, |
| 70 | + { |
| 71 | + "cell_type": "code", |
| 72 | + "execution_count": null, |
| 73 | + "metadata": {}, |
| 74 | + "outputs": [], |
| 75 | + "source": [ |
| 76 | + "# Exercise 2 — interactive dot plot\n", |
| 77 | + "sc.pl.dotplot(\n", |
| 78 | + " adata,\n", |
| 79 | + " var_names=[\"C1QA\", \"PSAP\", \"CD79A\", \"CD79B\", \"CST3\", \"LYZ\", \"ANXA1\", \"S100A4\"],\n", |
| 80 | + " groupby=\"bulk_labels\",\n", |
| 81 | + ")" |
| 82 | + ] |
| 83 | + }, |
| 84 | + { |
| 85 | + "cell_type": "code", |
| 86 | + "execution_count": null, |
| 87 | + "metadata": {}, |
| 88 | + "outputs": [], |
| 89 | + "source": [ |
| 90 | + "# Exercise 1 — your answer here\n", |
| 91 | + "vc_ex = VitessceConfig(schema_version=\"1.0.15\", name=\"Exercise 1\")\n", |
| 92 | + "ds_ex = vc_ex.add_dataset(name=\"PBMC\").add_object(AnnDataWrapper(\n", |
| 93 | + " adata_store=zarr_path,\n", |
| 94 | + " obs_set_paths=[\"obs/bulk_labels\"],\n", |
| 95 | + " obs_set_names=[\"Cell Type\"],\n", |
| 96 | + " obs_embedding_paths=[\"obsm/X_umap\"],\n", |
| 97 | + " obs_embedding_names=[\"UMAP\"],\n", |
| 98 | + " obs_feature_matrix_path=\"X\",\n", |
| 99 | + "))\n", |
| 100 | + "\n", |
| 101 | + "umap_ex = vc_ex.add_view(vt.SCATTERPLOT, dataset=ds_ex, mapping=\"UMAP\")\n", |
| 102 | + "violin_ex = vc_ex.add_view(vt.OBS_SET_FEATURE_VALUE_DISTRIBUTION, dataset=ds_ex)\n", |
| 103 | + "sets_ex = vc_ex.add_view(vt.OBS_SETS, dataset=ds_ex)\n", |
| 104 | + "\n", |
| 105 | + "vc_ex.layout(???) # TODO: arrange the three views\n", |
| 106 | + "vc_ex.widget()" |
| 107 | + ] |
| 108 | + }, |
| 109 | + { |
| 110 | + "cell_type": "markdown", |
| 111 | + "metadata": {}, |
| 112 | + "source": [ |
| 113 | + "## Exercise\n", |
| 114 | + "\n", |
| 115 | + "### Exercise 1 — Add a violin plot to the PBMC dashboard\n", |
| 116 | + "\n", |
| 117 | + "👉 **Starting from the `vc_hm` configuration** (the heatmap + UMAP example), add an `OBS_SET_FEATURE_VALUE_DISTRIBUTION` view showing violin plots.\n", |
| 118 | + "\n", |
| 119 | + "Steps:\n", |
| 120 | + "1. Add the violin view with `vc_hm.add_view(vt.OBS_SET_FEATURE_VALUE_DISTRIBUTION, dataset=ds_hm)`\n", |
| 121 | + "2. Include it in the layout, e.g., `(umap_hm | obs_sets3) / (heatmap | violin)`\n", |
| 122 | + "3. Re-run `vc_hm.widget()` to see the result\n", |
| 123 | + "\n", |
| 124 | + "```python\n", |
| 125 | + "# Template — fill in the blanks\n", |
| 126 | + "vc_ex = VitessceConfig(schema_version=\"1.0.15\", name=\"Exercise 1\")\n", |
| 127 | + "ds_ex = vc_ex.add_dataset(name=\"PBMC\").add_object(AnnDataWrapper(\n", |
| 128 | + " adata_store=zarr_path,\n", |
| 129 | + " obs_set_paths=[\"obs/bulk_labels\"],\n", |
| 130 | + " obs_set_names=[\"Cell Type\"],\n", |
| 131 | + " obs_embedding_paths=[\"obsm/X_umap\"],\n", |
| 132 | + " obs_embedding_names=[\"UMAP\"],\n", |
| 133 | + " obs_feature_matrix_path=\"X\",\n", |
| 134 | + "))\n", |
| 135 | + "\n", |
| 136 | + "umap_ex = vc_ex.add_view(vt.SCATTERPLOT, dataset=ds_ex, mapping=\"UMAP\")\n", |
| 137 | + "violin_ex = vc_ex.add_view(???, dataset=ds_ex) # Add the violin view\n", |
| 138 | + "sets_ex = vc_ex.add_view(vt.OBS_SETS, dataset=ds_ex)\n", |
| 139 | + "\n", |
| 140 | + "vc_ex.layout(???) # Arrange the views\n", |
| 141 | + "vc_ex.widget()\n", |
| 142 | + "```\n", |
| 143 | + "\n", |
| 144 | + "### Exercise 2 — Explore the dot plot interactively\n", |
| 145 | + "\n", |
| 146 | + "The dot plot view in Vitessce is interactive: you can click gene names to see them highlighted in other views. Run the cell below and try clicking a gene name in the dot plot row labels.\n", |
| 147 | + "\n", |
| 148 | + "```python\n", |
| 149 | + "sc.pl.dotplot(\n", |
| 150 | + " adata,\n", |
| 151 | + " var_names=[\"C1QA\", \"PSAP\", \"CD79A\", \"CD79B\", \"CST3\", \"LYZ\", \"ANXA1\", \"S100A4\"],\n", |
| 152 | + " groupby=\"bulk_labels\",\n", |
| 153 | + ")\n", |
| 154 | + "```\n", |
| 155 | + "\n", |
| 156 | + "**What to notice:**\n", |
| 157 | + "- Hovering over a dot shows the exact mean expression and percentage\n", |
| 158 | + "- Clicking a gene selects it as the active feature (visible in other linked views if you have a multi-view layout)" |
| 159 | + ] |
| 160 | + }, |
| 161 | + { |
| 162 | + "cell_type": "code", |
| 163 | + "execution_count": null, |
| 164 | + "metadata": {}, |
| 165 | + "outputs": [], |
| 166 | + "source": [ |
| 167 | + "vc_ctrl = VitessceConfig(schema_version=\"1.0.15\", name=\"Control Views Demo\")\n", |
| 168 | + "ds_ctrl = vc_ctrl.add_dataset(name=\"PBMC\").add_object(AnnDataWrapper(\n", |
| 169 | + " adata_store=zarr_path,\n", |
| 170 | + " obs_set_paths=[\"obs/bulk_labels\"],\n", |
| 171 | + " obs_set_names=[\"Cell Type\"],\n", |
| 172 | + " obs_embedding_paths=[\"obsm/X_umap\"],\n", |
| 173 | + " obs_embedding_names=[\"UMAP\"],\n", |
| 174 | + " obs_feature_matrix_path=\"X\",\n", |
| 175 | + "))\n", |
| 176 | + "\n", |
| 177 | + "umap_c = vc_ctrl.add_view(vt.SCATTERPLOT, dataset=ds_ctrl, mapping=\"UMAP\")\n", |
| 178 | + "sets_c = vc_ctrl.add_view(vt.OBS_SETS, dataset=ds_ctrl)\n", |
| 179 | + "genes_c = vc_ctrl.add_view(vt.FEATURE_LIST, dataset=ds_ctrl)\n", |
| 180 | + "status = vc_ctrl.add_view(vt.STATUS, dataset=ds_ctrl)\n", |
| 181 | + "\n", |
| 182 | + "# Status bar is typically compact; place it below the main content\n", |
| 183 | + "vc_ctrl.layout((umap_c | (sets_c / genes_c)) / status)\n", |
| 184 | + "vc_ctrl.widget()" |
| 185 | + ] |
| 186 | + }, |
| 187 | + { |
| 188 | + "cell_type": "markdown", |
| 189 | + "metadata": {}, |
| 190 | + "source": [ |
| 191 | + "## 4. Control views\n", |
| 192 | + "\n", |
| 193 | + "Control views don't render data directly but provide UI panels that let users interact with the visualization.\n", |
| 194 | + "\n", |
| 195 | + "| View type | Purpose |\n", |
| 196 | + "|---|---|\n", |
| 197 | + "| `vt.OBS_SETS` | Navigate cell-type hierarchy; select/deselect groups |\n", |
| 198 | + "| `vt.FEATURE_LIST` | Search for genes; click to color embeddings by expression |\n", |
| 199 | + "| `vt.STATUS` | Shows hover information — gene name, cell ID, expression value |\n", |
| 200 | + "| `\"layerControllerBeta\"` | Toggle image channels on/off, adjust contrast windows and colors |\n", |
| 201 | + "\n", |
| 202 | + "The example below adds a `STATUS` view below the UMAP. Hover over points in the scatterplot to see live cell information in the status bar." |
| 203 | + ] |
| 204 | + }, |
| 205 | + { |
| 206 | + "cell_type": "code", |
| 207 | + "execution_count": null, |
| 208 | + "metadata": {}, |
| 209 | + "outputs": [], |
| 210 | + "source": [ |
| 211 | + "import os, zipfile\n", |
| 212 | + "from os.path import join, isfile, isdir\n", |
| 213 | + "from urllib.request import urlretrieve\n", |
| 214 | + "import spatialdata_plot # noqa: F401\n", |
| 215 | + "\n", |
| 216 | + "data_dir = \"data\"\n", |
| 217 | + "sdata_filepath = join(data_dir, \"visium.spatialdata.zarr\")\n", |
| 218 | + "\n", |
| 219 | + "if not isdir(sdata_filepath):\n", |
| 220 | + " zip_filepath = join(data_dir, \"visium.spatialdata.zarr.zip\")\n", |
| 221 | + " if not isfile(zip_filepath):\n", |
| 222 | + " os.makedirs(data_dir, exist_ok=True)\n", |
| 223 | + " urlretrieve(\"https://data-2.vitessce.io/sdata-datasets/visium.spatialdata.zarr.zip\", zip_filepath)\n", |
| 224 | + " with zipfile.ZipFile(zip_filepath, \"r\") as z:\n", |
| 225 | + " z.extractall(data_dir)\n", |
| 226 | + " os.rename(join(data_dir, \"data.zarr\"), sdata_filepath)\n", |
| 227 | + "\n", |
| 228 | + "from spatialdata import read_zarr\n", |
| 229 | + "sdata = read_zarr(sdata_filepath)\n", |
| 230 | + "\n", |
| 231 | + "# Interactive spatial view colored by Fth1 expression\n", |
| 232 | + "sdata.pl.render_images(\"ST8059050_hires_image\") \\\n", |
| 233 | + " .pl.render_shapes(\"ST8059050\", color=\"Fth1\", table_name=\"table\") \\\n", |
| 234 | + " .pl.show(\"ST8059050\")" |
| 235 | + ] |
| 236 | + }, |
| 237 | + { |
| 238 | + "cell_type": "markdown", |
| 239 | + "metadata": {}, |
| 240 | + "source": [ |
| 241 | + "## 3. Spatial views\n", |
| 242 | + "\n", |
| 243 | + "### 3a. The spatial viewer (`spatialBeta`)\n", |
| 244 | + "\n", |
| 245 | + "The spatial viewer renders the tissue image and overlays data layers such as Visium spots colored by gene expression. Use `.pl.render_images()` and `.pl.render_shapes()` with EasyVitessce to compose the spatial view from a SpatialData object." |
| 246 | + ] |
| 247 | + }, |
| 248 | + { |
| 249 | + "cell_type": "code", |
| 250 | + "execution_count": null, |
| 251 | + "metadata": {}, |
| 252 | + "outputs": [], |
| 253 | + "source": [ |
| 254 | + "import os\n", |
| 255 | + "from os.path import join, isdir\n", |
| 256 | + "from vitessce import VitessceConfig, ViewType as vt, AnnDataWrapper\n", |
| 257 | + "from vitessce.data_utils import optimize_adata, VAR_CHUNK_SIZE\n", |
| 258 | + "\n", |
| 259 | + "zarr_path = join(\"data\", \"pbmc68k.zarr\")\n", |
| 260 | + "if not isdir(zarr_path):\n", |
| 261 | + " os.makedirs(\"data\", exist_ok=True)\n", |
| 262 | + " adata_opt = optimize_adata(\n", |
| 263 | + " adata,\n", |
| 264 | + " obs_cols=[\"bulk_labels\", \"louvain\"],\n", |
| 265 | + " obsm_keys=[\"X_umap\", \"X_pca\"],\n", |
| 266 | + " optimize_X=True,\n", |
| 267 | + " )\n", |
| 268 | + " adata_opt.write_zarr(zarr_path, chunks=[adata_opt.shape[0], VAR_CHUNK_SIZE])\n", |
| 269 | + "\n", |
| 270 | + "vc_hm = VitessceConfig(schema_version=\"1.0.15\", name=\"Heatmap + UMAP\")\n", |
| 271 | + "ds_hm = vc_hm.add_dataset(name=\"PBMC\").add_object(AnnDataWrapper(\n", |
| 272 | + " adata_store=zarr_path,\n", |
| 273 | + " obs_set_paths=[\"obs/bulk_labels\"],\n", |
| 274 | + " obs_set_names=[\"Cell Type\"],\n", |
| 275 | + " obs_embedding_paths=[\"obsm/X_umap\"],\n", |
| 276 | + " obs_embedding_names=[\"UMAP\"],\n", |
| 277 | + " obs_feature_matrix_path=\"X\",\n", |
| 278 | + "))\n", |
| 279 | + "\n", |
| 280 | + "umap_hm = vc_hm.add_view(vt.SCATTERPLOT, dataset=ds_hm, mapping=\"UMAP\")\n", |
| 281 | + "heatmap = vc_hm.add_view(vt.HEATMAP, dataset=ds_hm)\n", |
| 282 | + "obs_sets3 = vc_hm.add_view(vt.OBS_SETS, dataset=ds_hm)\n", |
| 283 | + "\n", |
| 284 | + "vc_hm.layout((umap_hm | obs_sets3) / heatmap)\n", |
| 285 | + "vc_hm.widget()" |
| 286 | + ] |
| 287 | + }, |
| 288 | + { |
| 289 | + "cell_type": "markdown", |
| 290 | + "metadata": {}, |
| 291 | + "source": [ |
| 292 | + "### 2c. Heatmap (`heatmap`)\n", |
| 293 | + "\n", |
| 294 | + "A **heatmap** renders a cells × genes expression matrix where color intensity encodes the expression level. Use it to compare expression patterns across many genes simultaneously and to validate cluster markers.\n", |
| 295 | + "\n", |
| 296 | + "The EasyVitessce heatmap is accessible via the low-level `VitessceConfig` API. Let's build a compact example." |
| 297 | + ] |
| 298 | + }, |
| 299 | + { |
| 300 | + "cell_type": "code", |
| 301 | + "execution_count": null, |
| 302 | + "metadata": {}, |
| 303 | + "outputs": [], |
| 304 | + "source": [ |
| 305 | + "sc.pl.violin(adata, keys=\"LYZ\", groupby=\"bulk_labels\")" |
| 306 | + ] |
| 307 | + }, |
| 308 | + { |
| 309 | + "cell_type": "markdown", |
| 310 | + "metadata": {}, |
| 311 | + "source": [ |
| 312 | + "### 2b. Violin / distribution plot (`obsSetFeatureValueDistribution`)\n", |
| 313 | + "\n", |
| 314 | + "A **violin plot** shows the full distribution of expression values for one gene across cell-type groups. It combines a box plot (median, quartiles) with a density estimate (the width of the violin shape).\n", |
| 315 | + "\n", |
| 316 | + "Use violin plots to understand whether expression differences between groups are large or variable." |
| 317 | + ] |
| 318 | + }, |
| 319 | + { |
| 320 | + "cell_type": "code", |
| 321 | + "execution_count": null, |
| 322 | + "metadata": {}, |
| 323 | + "outputs": [], |
| 324 | + "source": [ |
| 325 | + "sc.pl.dotplot(\n", |
| 326 | + " adata,\n", |
| 327 | + " var_names=[\"C1QA\", \"PSAP\", \"CD79A\", \"CD79B\", \"CST3\", \"LYZ\"],\n", |
| 328 | + " groupby=\"bulk_labels\",\n", |
| 329 | + ")" |
| 330 | + ] |
| 331 | + }, |
| 332 | + { |
| 333 | + "cell_type": "markdown", |
| 334 | + "metadata": {}, |
| 335 | + "source": [ |
| 336 | + "## 2. Expression views\n", |
| 337 | + "\n", |
| 338 | + "### 2a. Dot plot (`dotPlot`)\n", |
| 339 | + "\n", |
| 340 | + "A **dot plot** encodes two quantities simultaneously for each gene × cell-type combination:\n", |
| 341 | + "\n", |
| 342 | + "- **Dot size** — fraction of cells in the group that express the gene (above a threshold)\n", |
| 343 | + "- **Dot color** — mean expression level across all cells in the group\n", |
| 344 | + "\n", |
| 345 | + "Dot plots are useful for quickly comparing marker gene expression across many cell types." |
| 346 | + ] |
| 347 | + }, |
| 348 | + { |
| 349 | + "cell_type": "code", |
| 350 | + "execution_count": null, |
| 351 | + "metadata": {}, |
| 352 | + "outputs": [], |
| 353 | + "source": [ |
| 354 | + "# PCA colored by a gene — try changing CD79A to another gene name\n", |
| 355 | + "sc.pl.pca(adata, color=\"CD79A\")" |
| 356 | + ] |
| 357 | + }, |
| 358 | + { |
| 359 | + "cell_type": "code", |
| 360 | + "execution_count": null, |
| 361 | + "metadata": {}, |
| 362 | + "outputs": [], |
| 363 | + "source": [ |
| 364 | + "# UMAP colored by cell type label\n", |
| 365 | + "sc.pl.embedding(adata, basis=\"umap\", color=\"bulk_labels\")" |
| 366 | + ] |
| 367 | + }, |
| 368 | + { |
| 369 | + "cell_type": "markdown", |
| 370 | + "metadata": {}, |
| 371 | + "source": [ |
| 372 | + "## 1. Embedding views (`scatterplot`)\n", |
| 373 | + "\n", |
| 374 | + "A **scatterplot** view renders a 2D dimensionality reduction such as UMAP or PCA. Each point is one cell. Points can be colored by:\n", |
| 375 | + "\n", |
| 376 | + "- **Cell type / cluster label** — categorical coloring\n", |
| 377 | + "- **Gene expression** — continuous colormap from low (gray) to high (color)\n", |
| 378 | + "\n", |
| 379 | + "EasyVitessce hooks into `sc.pl.embedding()`. Pass `basis=` to choose the embedding and `color=` to choose what to encode with color." |
| 380 | + ] |
30 | 381 | } |
31 | 382 | ], |
32 | 383 | "metadata": { |
|
51 | 402 | "name": "python", |
52 | 403 | "nbconvert_exporter": "python", |
53 | 404 | "pygments_lexer": "ipython3", |
54 | | - "version": "3.8.16" |
| 405 | + "version": "3.12.5" |
55 | 406 | } |
56 | 407 | }, |
57 | 408 | "nbformat": 4, |
|
0 commit comments