diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c5a5f282..74cfbff5 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,5 +14,6 @@ jobs: run: | sudo apt-get update sudo apt-get install -y pandoc - - run: uv sync --extra docs --extra all + - run: uv sync --extra docs --extra notebook --extra all + - run: uv run marimo export html-wasm docs/notebooks/marimo-wasm.mo.py -o docs/_static/mo/marimo-wasm.mo.html --mode edit - run: uv run make html diff --git a/.github/workflows/test_widget.yml b/.github/workflows/test_widget.yml index b8c0fe02..333be5cd 100644 --- a/.github/workflows/test_widget.yml +++ b/.github/workflows/test_widget.yml @@ -29,11 +29,11 @@ jobs: - name: Install vitessce run: uv sync --extra dev --extra all --extra notebook - name: Export Jupyter notebook to HTML - run: uv run jupyter nbconvert --to=html --execute docs/notebooks/widget_from_dict.ipynb + run: uv run jupyter nbconvert --to=html --execute docs/notebooks/__ipynb__/widget_from_dict.ipynb - name: Export Marimo notebook to HTML - run: uv run marimo export html docs/notebooks/marimo.py -o docs/notebooks/marimo.html + run: uv run marimo export html docs/notebooks/marimo.mo.py -o docs/notebooks/marimo.mo.html - name: Export Marimo notebook to HTML-WASM - run: uv run marimo export html-wasm docs/notebooks/marimo-wasm.py -o docs/notebooks/marimo-wasm.html --mode edit + run: uv run marimo export html-wasm docs/notebooks/marimo-wasm.mo.py -o docs/notebooks/marimo-wasm.mo.html --mode edit - name: Check that widget renders in HTML output using Playwright run: pnpm exec playwright test working-directory: ./tests-widget \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4e008044..7059a3a7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,13 @@ tests/data/* !tests/data/test.ome.tif ./notebooks/ docs/notebooks/data/ +docs/notebooks/data-cropped/ +docs/notebooks/kpmp-2024/ +docs/notebooks/segmentations/ +docs/notebooks/*.json docs/notebooks/*.html +docs/notebooks/*.ipynb +docs/notebooks/*.zarr/ __pycache__/ .snakemake/ .coverage @@ -19,6 +25,9 @@ demos/*/data/ demos/*/vitessce.local.json demos/*/vitessce.remote.json .pytest_cache/ +__marimo__/ +assets/ +docs/_static/mo/ # Compiled javascript vitessce/static/ diff --git a/docs/_static/mo/.gitkeep b/docs/_static/mo/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/conf.py b/docs/conf.py index d14b41ab..229933a5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,6 +3,7 @@ import sys import glob from os.path import join +import json import vitessce @@ -123,9 +124,39 @@ def find_source(): # -- Strip notebook output ------------------------------------------------- -for filename in glob.glob(join('notebooks', '*.ipynb'), recursive=True): +for filename in glob.glob(join('notebooks', "__ipynb__", '*.ipynb'), recursive=True): ntbk = nbclean.NotebookCleaner(filename) ntbk.clear('stderr') ntbk.clear('output') ntbk.remove_cells(empty=True) + ntbk.remove_cells(search_text="import marimo as mo") ntbk.save(filename) + + # Add missing metadata, to enable the code to be interpreted as Python code + # for syntax highlighting when rendered by nbsphinx. + with open(filename, 'r') as f: + ntbk_json = json.load(f) + with open(filename, 'w') as f: + if len(ntbk_json['metadata']) == 0: + # If the metadata is empty, we add the default metadata. + # This is needed for nbsphinx to render the notebook correctly. + ntbk_json['metadata'] = { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + } + json.dump(ntbk_json, f, indent=2) diff --git a/docs/data_examples.rst b/docs/data_examples.rst index 4553453c..f39979a1 100644 --- a/docs/data_examples.rst +++ b/docs/data_examples.rst @@ -5,7 +5,7 @@ Data preparation examples .. toctree:: :maxdepth: 2 - notebooks/data_export_s3 - notebooks/data_export_files - notebooks/widget_brain_with_base_dir - notebooks/widget_brain_h5ad \ No newline at end of file + notebooks/__ipynb__/data_export_s3 + notebooks/__ipynb__/data_export_files + notebooks/__ipynb__/widget_brain_with_base_dir + notebooks/__ipynb__/widget_brain_h5ad \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/cellbrowser_to_vitessce_config_conversion.ipynb b/docs/notebooks/__ipynb__/cellbrowser_to_vitessce_config_conversion.ipynb new file mode 100644 index 00000000..31abf63b --- /dev/null +++ b/docs/notebooks/__ipynb__/cellbrowser_to_vitessce_config_conversion.ipynb @@ -0,0 +1,190 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Load UCSC Cell Browser project in Vitessce" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "This notebook shows you how to use the `convert_cell_browser_project_to_anndata` function, which allows you to take an existing project, published in https://cells.ucsc.edu/ and:\n", + "1. Convert it into the AnnData format that is supported by Vitessce\n", + "2. Save the AnnData object as a Zarr store\n", + "3. Configure Vitessce with the AnnData-Zarr store\n", + "4. Render a Vitessce widget based on the config (step 3) directly in the notebook.\n", + "\n", + "The dataset that you choose to convert needs to be a valid UCSC Cell Browser \"project\", accessible from https://cells.ucsc.edu/, with a configuration available in https://github.com/ucscGenomeBrowser/cellbrowser-confs\n", + "\n", + "The `convert_cell_browser_project_to_anndata` function takes the name of that project as an input. For example, to convert this project, https://cells.ucsc.edu/?ds=adultPancreas, you will neeed to pass `\"adultPancreas\"` as the project name." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "from os.path import join\n", + "from vitessce import (\n", + " convert_cell_browser_project_to_anndata,\n", + " AnnDataWrapper,\n", + " VitessceConfig,\n", + ")\n", + "from vitessce.data_utils import VAR_CHUNK_SIZE" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Convert UCSC Cell Browser project to a format that is supported by Vitessce\n", + "#### Output:\n", + "An AnnData object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "# Example run, coverting \"adultPancreas\" project:\n", + "adata = convert_cell_browser_project_to_anndata(project_name=\"adultPancreas\", keep_only_marker_genes=True)" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Save the AnnData object as a Zarr store" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "SFPL", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_filepath = join(\"data\", \"out.adata.zarr\")\n", + "os.makedirs(os.path.dirname(zarr_filepath), exist_ok=True)\n", + "adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "markdown", + "id": "BYtC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Configure Vitessce with the AnnData-Zarr store" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "RGSE", + "metadata": {}, + "outputs": [], + "source": [ + "anndata_wrapper_inst = AnnDataWrapper(\n", + " adata_path=zarr_filepath,\n", + " obs_feature_matrix_path=\"X\",\n", + " obs_embedding_paths=[\"obsm/X_tsne\"],\n", + " obs_embedding_names=[\"t-SNE\"],\n", + " obs_set_paths=[\"obs/cluster\", \"obs/age\"],\n", + " obs_set_names=[\"cluster\", \"age\"],\n", + ")\n", + "vc = VitessceConfig(schema_version=\"1.0.15\", name=\"Vitessce configuration for CellBrowser project adultPancreas\")\n", + "anndata_wrapper_inst.auto_view_config(vc)" + ] + }, + { + "cell_type": "markdown", + "id": "Kclp", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Render the Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "emfo", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/config_to_python.ipynb b/docs/notebooks/__ipynb__/config_to_python.ipynb new file mode 100644 index 00000000..084378cf --- /dev/null +++ b/docs/notebooks/__ipynb__/config_to_python.ipynb @@ -0,0 +1,216 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Generate Python code to reconstruct a VitessceConfig instance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import VitessceConfig, VitessceChainableConfig, VitessceConfigDatasetFile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "from example_configs import dries as dries_config" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Load a view config from a dict representation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig.from_dict(dries_config)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Print to JSON" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "print(json.dumps(vc.to_dict(), indent=2))" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Print to Python\n", + "\n", + "The `vc.to_python` function generates formatted Python code which can be used to re-generate the `vc` instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "imports, code = vc.to_python()" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "The first value returned is a list of classes used by the code snippet." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "emfo", + "metadata": {}, + "outputs": [], + "source": [ + "print(code)" + ] + }, + { + "cell_type": "markdown", + "id": "Hstk", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "The second value is the code snippet. When evaluated, the result will be a new `VitessceConfig` instance." + ] + }, + { + "cell_type": "markdown", + "id": "nWHF", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Evaluate the code and render a Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "iLit", + "metadata": {}, + "outputs": [], + "source": [ + "reconstructed_vc = eval(code)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ZHCJ", + "metadata": {}, + "outputs": [], + "source": [ + "reconstructed_vc.widget()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/data_conversion.ipynb b/docs/notebooks/__ipynb__/data_conversion.ipynb new file mode 100644 index 00000000..bb1c6f96 --- /dev/null +++ b/docs/notebooks/__ipynb__/data_conversion.ipynb @@ -0,0 +1,111 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Convert data manually" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "When running the Vitessce widget, data is converted on-the-fly into formats that the Vitessce JavaScript component can render.\n", + "The converted data is stored in temporary files, preventing long-term use of the converted files.\n", + "\n", + "However, the data conversion utilities used by the widget are exposed so that their outputs can be saved to regular files.\n", + "This allows the files to be saved for future use with the Vitessce web application by serving the files locally or moving the files onto an object storage system such as AWS S3 (for long-term storage and data sharing).\n", + "\n", + "This notebook demonstrates how to save the processed outputs of the `AnnDataWrapper` and `SnapWrapper` classes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import SnapWrapper\n", + "from os.path import join\n", + "from scipy.io import mmread\n", + "import pandas as pd\n", + "import numpy as np\n", + "import json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "mtx = mmread(join('data', 'snapatac', 'filtered_cell_by_bin.mtx'))\n", + "barcodes_df = pd.read_csv(join('data', 'snapatac', 'barcodes.txt'), header=None)\n", + "bins_df = pd.read_csv(join('data', 'snapatac', 'bins.txt'), header=None)\n", + "clusters_df = pd.read_csv(join('data', 'snapatac', 'umap_coords_clusters.csv'), index_col=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_filepath = join('data', 'snapatac', 'out.snap.multires.zarr')\n", + "\n", + "w = SnapWrapper(mtx, barcodes_df, bins_df, clusters_df)\n", + "\n", + "cells_json = w.create_cells_json()\n", + "cell_sets_json = w.create_cell_sets_json()\n", + "\n", + "with open(join('data', 'snapatac', 'out.cells.json'), 'w') as f:\n", + " json.dump(cells_json, f)\n", + "\n", + "with open(join('data', 'snapatac', 'out.cell-sets.json'), 'w') as f:\n", + " json.dump(cell_sets_json, f)\n", + "\n", + "\n", + "w.create_genomic_multivec_zarr(zarr_filepath)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/data_export_files.ipynb b/docs/notebooks/__ipynb__/data_export_files.ipynb new file mode 100644 index 00000000..e5a282b2 --- /dev/null +++ b/docs/notebooks/__ipynb__/data_export_files.ipynb @@ -0,0 +1,276 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Export data to local files" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "from urllib.parse import quote_plus\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "\n", + "from vitessce import (\n", + " VitessceWidget,\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")\n", + "from vitessce.data_utils import (\n", + " optimize_adata,\n", + " VAR_CHUNK_SIZE,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Download and process data\n", + "\n", + "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", + "if not isfile(adata_filepath):\n", + " os.makedirs(\"data\", exist_ok=True)\n", + " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)\n", + "\n", + "adata = read_h5ad(adata_filepath)\n", + "top_dispersion = adata.var[\"dispersions_norm\"][\n", + " sorted(\n", + " range(len(adata.var[\"dispersions_norm\"])),\n", + " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", + " )[-51:][0]\n", + "]\n", + "adata.var[\"top_highly_variable\"] = (\n", + " adata.var[\"dispersions_norm\"] > top_dispersion\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_filepath = join('data', 'habib17.processed.zarr')\n", + "if not isdir(zarr_filepath):\n", + " adata_1 = optimize_adata(adata, obs_cols=['CellType'], obsm_keys=['X_umap'], var_cols=['top_highly_variable'], optimize_X=True)\n", + " adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Create the Vitessce configuration" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Set up the configuration by adding the views and datasets of interest." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', description='COVID-19 Healthy Donor Brain')\n", + "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", + " adata_path=zarr_filepath,\n", + " obs_embedding_paths=[\"obsm/X_umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/CellType\"],\n", + " obs_set_names=[\"Cell Type\"],\n", + " obs_feature_matrix_path=\"X\",\n", + " feature_filter_path=\"var/top_highly_variable\"\n", + "))\n", + "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"X_umap\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)\n", + "vc.layout((scatterplot | (cell_sets / genes)) / heatmap);" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Export files to a local directory\n", + "\n", + "The `.export(to='files')` method on the view config instance will export files to the specified directory `out_dir`. The `base_url` parameter is required so that the file URLs in the view config point to the location where you ultimately intend to serve the files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "config_dict = vc.export(to='files', base_url='http://localhost:3000', out_dir='./test')" + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Serve the files" + ] + }, + { + "cell_type": "markdown", + "id": "Hstk", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Now that the files have been saved to the `./test` directory, they can be served by any static web server.\n", + "\n", + "If you would like to serve the files locally, we recommend [http-server](https://github.com/http-party/http-server) which can be installed with NPM or Homebrew:\n", + "```sh\n", + "cd test\n", + "http-server ./ --cors -p 3000\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "nWHF", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 6. View on vitessce.io\n", + "\n", + "The returned view config dict can be converted to a URL, and if the files are served on the internet (rather than locally), this URL can be used to share the interactive visualizations with colleagues." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "iLit", + "metadata": {}, + "outputs": [], + "source": [ + "vitessce_url = \"http://vitessce.io/?url=data:,\" + quote_plus(json.dumps(config_dict))\n", + "import webbrowser\n", + "webbrowser.open(vitessce_url)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/data_export_s3.ipynb b/docs/notebooks/__ipynb__/data_export_s3.ipynb new file mode 100644 index 00000000..5ed073d9 --- /dev/null +++ b/docs/notebooks/__ipynb__/data_export_s3.ipynb @@ -0,0 +1,271 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Export data to AWS S3" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import boto3\n", + "import json\n", + "from urllib.parse import quote_plus\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "\n", + "from vitessce import (\n", + " VitessceWidget,\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")\n", + "from vitessce.data_utils import (\n", + " optimize_adata,\n", + " VAR_CHUNK_SIZE,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Download and process data\n", + "\n", + "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", + "if not isfile(adata_filepath):\n", + " os.makedirs(\"data\", exist_ok=True)\n", + " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)\n", + "\n", + "adata = read_h5ad(adata_filepath)\n", + "top_dispersion = adata.var[\"dispersions_norm\"][\n", + " sorted(\n", + " range(len(adata.var[\"dispersions_norm\"])),\n", + " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", + " )[-51:][0]\n", + "]\n", + "adata.var[\"top_highly_variable\"] = (\n", + " adata.var[\"dispersions_norm\"] > top_dispersion\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_filepath = join('data', 'habib17.processed.zarr')\n", + "if not isdir(zarr_filepath):\n", + " adata_1 = optimize_adata(adata, obs_cols=['CellType'], obsm_keys=['X_umap'], var_cols=['top_highly_variable'], optimize_X=True)\n", + " adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Create the Vitessce configuration" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Set up the configuration by adding the views and datasets of interest." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', description='COVID-19 Healthy Donor Brain')\n", + "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", + " adata_path=zarr_filepath,\n", + " obs_embedding_paths=[\"obsm/X_umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/CellType\"],\n", + " obs_set_names=[\"Cell Type\"],\n", + " obs_feature_matrix_path=\"X\",\n", + " feature_filter_path=\"var/top_highly_variable\"\n", + "))\n", + "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)\n", + "vc.layout((scatterplot | (cell_sets / genes)) / heatmap);" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Create a `boto3` resource with S3 credentials" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "s3 = boto3.resource(\n", + " service_name='s3',\n", + " aws_access_key_id=os.environ['VITESSCE_S3_ACCESS_KEY_ID'],\n", + " aws_secret_access_key=os.environ['VITESSCE_S3_SECRET_ACCESS_KEY'],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Upload files to S3\n", + "\n", + "The `.export(to='S3')` method on the view config instance will upload all data objects to the specified bucket. Then, the processed view config will be returned as a `dict`, with the file URLs filled in, pointing to the S3 bucket files. For more information about configuring the S3 bucket so that files are accessible over the internet, visit the \"Hosting Data\" page of our core documentation site." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Hstk", + "metadata": {}, + "outputs": [], + "source": [ + "config_dict = vc.export(to='S3', s3=s3, bucket_name='vitessce-export-examples', prefix='test')" + ] + }, + { + "cell_type": "markdown", + "id": "nWHF", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 6. View on vitessce.io\n", + "\n", + "The returned view config dict can be converted to a URL, and can be used to share the interactive visualizations with colleagues." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "iLit", + "metadata": {}, + "outputs": [], + "source": [ + "vitessce_url = \"http://vitessce.io/?url=data:,\" + quote_plus(json.dumps(config_dict))\n", + "import webbrowser\n", + "webbrowser.open(vitessce_url)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/example_configs.py b/docs/notebooks/__ipynb__/example_configs.py new file mode 100644 index 00000000..c965f282 --- /dev/null +++ b/docs/notebooks/__ipynb__/example_configs.py @@ -0,0 +1,107 @@ +dries = { + "name": "Dries", + "version": "1.0.0", + "description": "Giotto, a pipeline for integrative analysis and visualization of single-cell spatial transcriptomic data", + "public": True, + "datasets": [ + { + "uid": 'dries-2019', + "name": 'Dries 2019', + "files": [ + { + "type": "cells", + "fileType": "cells.json", + "url": "https://s3.amazonaws.com/vitessce-data/0.0.31/master_release/dries/dries.cells.json" + }, + { + "type": "cell-sets", + "fileType": "cell-sets.json", + "url": "https://s3.amazonaws.com/vitessce-data/0.0.31/master_release/dries/dries.cell-sets.json" + } + ] + } + ], + "initStrategy": "auto", + "coordinationSpace": { + "embeddingType": { + "TSNE": 't-SNE', + "UMAP": 'UMAP', + }, + "embeddingZoom": { + "TSNE": 3, + "UMAP": 3, + }, + "spatialZoom": { + "A": -4.4, + }, + "spatialTargetX": { + "A": 3800, + }, + "spatialTargetY": { + "A": -900, + }, + }, + "layout": [ + { + "component": "description", + "props": { + "description": "Giotto, a pipeline for integrative analysis and visualization of single-cell spatial transcriptomic data" + }, + "x": 9, + "y": 0, + "w": 3, + "h": 4 + }, + { + "component": "cellSets", + "x": 9, + "y": 4, + "w": 3, + "h": 4 + }, + { + "component": "cellSetSizes", + "x": 5, + "y": 4, + "w": 4, + "h": 4 + }, + { + "component": "scatterplot", + "coordinationScopes": { + "embeddingType": 'TSNE', + "embeddingZoom": 'TSNE' + }, + "x": 0, + "y": 2, + "w": 5, + "h": 4 + }, + { + "component": "spatial", + "props": { + "cellRadius": 50 + }, + "coordinationScopes": { + "spatialZoom": 'A', + "spatialTargetX": 'A', + "spatialTargetY": 'A' + }, + "x": 5, + "y": 0, + "w": 4, + "h": 4 + }, + { + "component": "scatterplot", + "coordinationScopes": { + "embeddingType": 'UMAP', + "embeddingZoom": 'UMAP' + }, + "x": 0, + "y": 0, + "w": 5, + "h": 4 + } + ] +} diff --git a/docs/notebooks/__ipynb__/page_mode_comparative.ipynb b/docs/notebooks/__ipynb__/page_mode_comparative.ipynb new file mode 100644 index 00000000..ec439c1a --- /dev/null +++ b/docs/notebooks/__ipynb__/page_mode_comparative.ipynb @@ -0,0 +1,506 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of ComparativeData object" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "from oxc_py import transform\n", + "from vitessce import VitessceConfig, hconcat, vconcat" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure the data and views" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "# Reference: https://github.com/vitessce/vitessce/blob/main/examples/configs/src/view-configs/kpmp-premiere.js" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "base_url = 'https://storage.googleapis.com/vitessce-demo-data/kpmp-jan-2025/kpmp_premiere_20250330.adata.zarr'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.17\", name='Lake et al.')\n", + "\n", + "dataset = vc.add_dataset('lake_et_al').add_file(\n", + " file_type='comparisonMetadata.anndata.zarr',\n", + " url=base_url,\n", + " options={\n", + " \"path\": 'uns/comparison_metadata',\n", + " },\n", + " coordination_values={\n", + " \"obsType\": 'cell',\n", + " \"sampleType\": 'sample',\n", + " },\n", + ").add_file(\n", + " file_type='comparativeFeatureStats.anndata.zarr',\n", + " url=base_url,\n", + " options= {\n", + " \"metadataPath\": 'uns/comparison_metadata',\n", + " \"indexColumn\": 'names',\n", + " \"pValueColumn\": 'pvals_adj',\n", + " \"foldChangeColumn\": 'logfoldchanges',\n", + " \"pValueAdjusted\": True,\n", + " \"foldChangeTransformation\": 'log2',\n", + " },\n", + " coordination_values={\n", + " \"obsType\": 'cell',\n", + " \"sampleType\": 'sample',\n", + " \"featureType\": 'gene',\n", + " },\n", + ").add_file(\n", + " file_type= 'comparativeObsSetStats.anndata.zarr',\n", + " url= base_url,\n", + " options= {\n", + " \"metadataPath\": 'uns/comparison_metadata',\n", + " \"indexColumn\": 'Cell Type',\n", + " \"interceptExpectedSampleColumn\": 'Expected Sample_intercept',\n", + " \"effectExpectedSampleColumn\": 'Expected Sample_effect',\n", + " \"foldChangeColumn\": 'log2-fold change',\n", + " \"foldChangeTransformation\": 'log2',\n", + " \"isCredibleEffectColumn\": 'is_credible_effect',\n", + " },\n", + " coordination_values= {\n", + " \"obsType\": 'cell',\n", + " \"sampleType\": 'sample',\n", + " },\n", + ").add_file(\n", + " file_type='comparativeFeatureSetStats.anndata.zarr',\n", + " url=base_url,\n", + " options= {\n", + " \"metadataPath\": 'uns/comparison_metadata',\n", + " \"indexColumn\": 'pathway_name',\n", + " \"termColumn\": 'pathway_term',\n", + " \"pValueColumn\": 'pvals_adj',\n", + " \"pValueAdjusted\": True,\n", + " \"analysisType\": 'pertpy_hypergeometric',\n", + " \"featureSetLibrary\": 'Reactome_2022',\n", + " },\n", + " coordination_values= {\n", + " \"obsType\": 'cell',\n", + " \"featureType\": 'gene',\n", + " \"sampleType\": 'sample',\n", + " },\n", + ").add_file(\n", + " file_type='anndata.zarr',\n", + " url=base_url,\n", + " coordination_values={\n", + " \"obsType\": 'cell',\n", + " \"featureType\": 'gene',\n", + " \"featureValueType\": 'expression',\n", + " \"sampleType\": 'sample',\n", + " },\n", + " options={\n", + " \"obsFeatureMatrix\": {\n", + " \"path\": 'layers/pearson_residuals',\n", + " },\n", + " \"obsEmbedding\": [\n", + " {\n", + " \"path\": 'obsm/X_densmap',\n", + " \"embeddingType\": 'densMAP',\n", + " },\n", + " ],\n", + " \"obsSets\": [\n", + " {\n", + " \"name\": 'Cell Type',\n", + " \"path\": 'obs/cell_type',\n", + " },\n", + " {\n", + " \"name\": 'Subclass L1',\n", + " \"path\": 'obs/subclass_l1',\n", + " },\n", + " {\n", + " \"name\": 'Subclass L2',\n", + " \"path\": 'obs/subclass_l2',\n", + " },\n", + " {\n", + " \"name\": 'Donor ID',\n", + " \"path\": 'obs/donor_id',\n", + " },\n", + " ],\n", + " \"sampleEdges\": {\n", + " \"path\": 'obs/SampleID',\n", + " },\n", + " },\n", + ").add_file(\n", + " file_type='sampleSets.anndata.zarr',\n", + " url=f\"{base_url}/uns/__all__.samples\",\n", + " options={\n", + " \"sampleSets\": [\n", + " {\n", + " \"name\": 'Disease Type',\n", + " \"path\": 'diseasetype',\n", + " },\n", + " {\n", + " \"name\": 'Adjudicated Category',\n", + " \"path\": 'AdjudicatedCategory',\n", + " },\n", + " {\n", + " \"name\": 'Enrollment Category',\n", + " \"path\": 'EnrollmentCategory',\n", + " },\n", + " ],\n", + " },\n", + " coordination_values= {\n", + " \"sampleType\": 'sample',\n", + " },\n", + ")\n", + "\n", + "biomarkerSelect = vc.add_view('biomarkerSelect', dataset=dataset, uid='biomarker-select')\n", + "comparativeHeading = vc.add_view('comparativeHeading', dataset=dataset, uid='comparative-heading')\n", + "dualScatterplot = vc.add_view('dualScatterplot', dataset=dataset, uid='scatterplot')\n", + "obsSets = vc.add_view('obsSets', dataset=dataset, uid='cell-sets')\n", + "sampleSets = vc.add_view('sampleSetPairManager', dataset=dataset, uid='sample-sets')\n", + "obsSetSizes = vc.add_view('obsSetSizes', dataset=dataset)\n", + "featureList = vc.add_view('featureList', dataset=dataset)\n", + "violinPlots = vc.add_view('obsSetFeatureValueDistribution', dataset=dataset, uid='violin-plot')\n", + "dotPlot = vc.add_view('dotPlot', dataset=dataset, uid='dot-plot')\n", + "treemap = vc.add_view('treemap', dataset=dataset, uid='treemap')\n", + "volcanoPlot = vc.add_view('volcanoPlot', dataset=dataset, uid='volcano-plot')\n", + "volcanoPlotTable = vc.add_view('featureStatsTable', dataset=dataset, uid='volcano-plot-table')\n", + "obsSetCompositionBarPlot = vc.add_view('obsSetCompositionBarPlot', dataset=dataset, uid='sccoda-plot')\n", + "featureSetEnrichmentBarPlot = vc.add_view('featureSetEnrichmentBarPlot', dataset=dataset, uid='pathways-plot')\n", + "\n", + "[sampleSetScope_caseControl] = vc.add_coordination('sampleSetSelection')\n", + "sampleSetScope_caseControl.set_value([['Disease Type', 'CKD'], ['Disease Type', 'Reference']])\n", + "\n", + "[featureSelectionScope] = vc.add_coordination('featureSelection')\n", + "featureSelectionScope.set_value(['UMOD', 'NPHS2'])\n", + "\n", + "vc.link_views_by_dict([dualScatterplot], {\n", + " \"embeddingType\": 'densMAP',\n", + " \"embeddingContoursVisible\": True,\n", + " \"embeddingPointsVisible\": False,\n", + " \"embeddingObsSetLabelsVisible\": True,\n", + "}, meta=False);\n", + "\n", + "\n", + "vc.link_views([biomarkerSelect, dualScatterplot, obsSets, obsSetSizes, featureList, violinPlots, dotPlot, treemap, volcanoPlot, volcanoPlotTable, comparativeHeading, obsSetCompositionBarPlot, featureSetEnrichmentBarPlot, sampleSets], ['sampleType'], ['sample'])\n", + "\n", + "vc.link_views_by_dict([biomarkerSelect, dualScatterplot, obsSets, obsSetSizes, featureList, violinPlots, dotPlot, treemap, volcanoPlot, volcanoPlotTable, comparativeHeading, obsSetCompositionBarPlot, featureSetEnrichmentBarPlot, sampleSets], {\n", + " \"sampleSetSelection\": sampleSetScope_caseControl,\n", + " \"featureSelection\": featureSelectionScope,\n", + "}, meta=False)\n", + "\n", + "vc.link_views_by_dict([dualScatterplot, violinPlots, featureList, dotPlot], {\n", + " # \"featureSelection\": ['UMOD', 'NPHS2'], // , 'ENSG00000074803', 'ENSG00000164825'],\n", + " \"obsColorEncoding\": 'geneSelection',\n", + " \"featureValueColormap\": 'jet',\n", + " \"featureValueColormapRange\": [0, 0.25],\n", + " \"featureAggregationStrategy\": None,\n", + "}, meta=False)\n", + "\n", + "vc.layout(hconcat(\n", + " vconcat(dualScatterplot, biomarkerSelect, comparativeHeading, obsSets, obsSetSizes, featureList),\n", + " vconcat(treemap, featureSetEnrichmentBarPlot, violinPlots, dotPlot, obsSetCompositionBarPlot, sampleSets),\n", + " volcanoPlotTable,\n", + "));" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Define the page layout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "PAGE_ESM = transform(\"\"\"\n", + "import clsx from \"https://unpkg.com/clsx@1.1.1/dist/clsx.m.js\";\n", + "\n", + "function createPage(utilsForPages) {\n", + " const {\n", + " React,\n", + " usePageModeView,\n", + " } = utilsForPages;\n", + " function PageComponent(props) {\n", + " const BiomarkerSelect = usePageModeView('biomarker-select');\n", + " const ComparativeHeading = usePageModeView('comparative-heading');\n", + " const CellSets = usePageModeView('cell-sets');\n", + " const SampleSets = usePageModeView('sample-sets');\n", + " const DualScatterplot = usePageModeView('scatterplot');\n", + " const ViolinPlot = usePageModeView('violin-plot');\n", + " const DotPlot = usePageModeView('dot-plot');\n", + " const Treemap = usePageModeView('treemap');\n", + " const VolcanoPlot = usePageModeView('volcano-plot');\n", + " const VolcanoPlotTable = usePageModeView('volcano-plot-table');\n", + " const SccodaPlot = usePageModeView('sccoda-plot');\n", + " const PathwaysPlot = usePageModeView('pathways-plot');\n", + "\n", + "\n", + " return (\n", + " <>\n", + " \n", + "
\n", + "
\n", + "

Comparative visualization of single-cell atlas data

\n", + " \n", + "
\n", + "
\n", + "\n", + "
\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "

This view contains a treemap visualization to communicate cell type composition in each of the selected sample groups.

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "

This view displays the results of a cell type composition analysis performed using the ScCODA algorithm (B\u00fcttner et al. 2021). Cell types with significantly different composition between the selected sample groups are displayed opaque while not-signficant results are displayed with transparent bars. The single outlined bar denotes the automatically-selected reference cell type.

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "

This view displays differential expression test results, performed using the rank_genes_groups function from Scanpy (Wolf et al. 2018) with method "wilcoxon".

The arrows on the bottom left and bottom right denote the direction of the effect. Click a point in the plot to select the corresponding gene.

Note that differential expression tests have been run for each cell type separately, so the each gene can appear multiple times (once per cell type). If there are too many points on the plot, cell types can be selected to filter the points.

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "

This view displays differential expression test results in tabular form. Click a row in the table to select the corresponding gene.

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "

This view displays gene set enrichment test results based on the differential expression results. Gene set enrichment tests have been performed using Reactome 2022 pathway gene sets from BlitzGSEA (Lachmann et al. 2022) via the hypergeometric function of Pertpy (Heumos et al. 2024).

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "

This view contains contour scatterplots which display the results of a density-preserving dimensionality reduction (Narayan et al. 2021). Contour opacities correspond to the shown percentile thresholds.

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "

This dot plot view displays gene expression values per cell type and sample group for the selected biomarkers.

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "

This violin plot view displays gene expression values per cell type and sample group for the selected biomarker.

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + " {/*

Neighborhood-level representations

\n", + "

TODO

\n", + "

Segmented instance-level representations

\n", + "

TODO

\n", + "

Image-level representations

\n", + "

TODO

\n", + "

Participant-level representations

\n", + "

TODO

*/}\n", + "
\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + " );\n", + " }\n", + " return PageComponent;\n", + "}\n", + "export default { createPage };\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Render page as widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget(page_esm=PAGE_ESM, page_mode=True, height=4700, prevent_scroll=False)\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/page_mode_example.ipynb b/docs/notebooks/__ipynb__/page_mode_example.ipynb new file mode 100644 index 00000000..86394336 --- /dev/null +++ b/docs/notebooks/__ipynb__/page_mode_example.ipynb @@ -0,0 +1,226 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Page mode example" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + " CsvWrapper,\n", + ")\n", + "from oxc_py import transform" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure the data and views" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "url = 'https://storage.googleapis.com/vitessce-demo-data/anndata-test/pbmc3k_processed.zarr'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.17\", name='PBMC Reference')\n", + "dataset = vc.add_dataset(name='PBMC 3k').add_object(\n", + " AnnDataWrapper(\n", + " adata_url=url,\n", + " obs_set_paths=[\"obs/louvain\"],\n", + " obs_set_names=[\"Louvain\"],\n", + " obs_embedding_paths=[\"obsm/X_umap\", \"obsm/X_pca\"],\n", + " obs_embedding_names=[\"UMAP\", \"PCA\"],\n", + " obs_feature_matrix_path=\"X\"\n", + " )\n", + ")\n", + "\n", + "umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\", uid=\"scatterplot-umap\")\n", + "pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"PCA\", uid=\"scatterplot-pca\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset, uid=\"cell-sets\")\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset, uid=\"gene-list\")\n", + "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset, uid=\"heatmap\")\n", + "\n", + "vc.layout((umap / pca) | ((cell_sets | genes) / heatmap));" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Define the page layout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "SFPL", + "metadata": {}, + "outputs": [], + "source": [ + "PAGE_ESM = transform(\"\"\"\n", + "function createPage(utilsForPages) {\n", + " const {\n", + " React,\n", + " usePageModeView,\n", + " } = utilsForPages;\n", + " function PageComponent(props) {\n", + " const ScatterplotUmap = usePageModeView('scatterplot-umap');\n", + " const ScatterplotPca = usePageModeView('scatterplot-pca');\n", + " const CellSets = usePageModeView('cell-sets');\n", + " const GeneList = usePageModeView('gene-list');\n", + " const Heatmap = usePageModeView('heatmap');\n", + "\n", + " return (\n", + " <>\n", + " \n", + "
\n", + "
\n", + "

This is an arbitrary HTML element with custom CSS

\n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Another HTML element

\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + " \n", + " \n", + "
\n", + "
\n", + "\n", + " \n", + " );\n", + " }\n", + " return PageComponent;\n", + "}\n", + "export default { createPage };\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "id": "BYtC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Render page as widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "RGSE", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget(page_esm=PAGE_ESM, page_mode=True, height=1100)\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/spatial_data.ipynb b/docs/notebooks/__ipynb__/spatial_data.ipynb new file mode 100644 index 00000000..6e7dde80 --- /dev/null +++ b/docs/notebooks/__ipynb__/spatial_data.ipynb @@ -0,0 +1,188 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "import zipfile\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " CoordinationLevel as CL,\n", + " SpatialDataWrapper,\n", + " get_initial_coordination_scope_prefix\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "data_dir = \"data\"\n", + "zip_filepath = join(data_dir, \"visium.spatialdata.zarr.zip\")\n", + "spatialdata_filepath = join(data_dir, \"visium.spatialdata.zarr\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "if not isdir(spatialdata_filepath):\n", + " if not isfile(zip_filepath):\n", + " os.makedirs(data_dir, exist_ok=True)\n", + " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/visium_associated_xenium_io.zip', zip_filepath)\n", + " with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n", + " zip_ref.extractall(data_dir)\n", + " os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(\n", + " schema_version=\"1.0.18\",\n", + " name='Visium SpatialData Demo (visium_associated_xenium_io)',\n", + ")\n", + "# Add data to the configuration:\n", + "wrapper = SpatialDataWrapper(\n", + " sdata_path=spatialdata_filepath,\n", + " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", + " image_path=\"images/CytAssist_FFPE_Human_Breast_Cancer_full_image\",\n", + " table_path=\"tables/table\",\n", + " obs_feature_matrix_path=\"tables/table/X\",\n", + " obs_spots_path=\"shapes/CytAssist_FFPE_Human_Breast_Cancer\",\n", + " region=\"CytAssist_FFPE_Human_Breast_Cancer\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " # The following tells Vitessce to consider each observation as a \"spot\"\n", + " \"obsType\": \"spot\",\n", + " }\n", + ")\n", + "dataset = vc.add_dataset(name='Breast Cancer Visium').add_object(wrapper)\n", + "\n", + "# Add views (visualizations) to the configuration:\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "feature_list = vc.add_view(vt.FEATURE_LIST, dataset=dataset)\n", + "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'imageLayer': CL([{\n", + " 'photometricInterpretation': 'RGB',\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", + "obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", + "vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], [wrapper.obs_type_label])\n", + "\n", + "# Layout the views\n", + "vc.layout(spatial | (feature_list / layer_controller / obs_sets));" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Render the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/spatial_data_blobs.ipynb b/docs/notebooks/__ipynb__/spatial_data_blobs.ipynb new file mode 100644 index 00000000..f80fd43d --- /dev/null +++ b/docs/notebooks/__ipynb__/spatial_data_blobs.ipynb @@ -0,0 +1,220 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a SpatialData object, blobs example" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import spatialdata\n", + "from os.path import join" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "sdata = spatialdata.datasets.blobs()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "spatialdata_filepath = join(\"data\", \"blobs.spatialdata.zarr\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "sdata.write(spatialdata_filepath, overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " CoordinationLevel as CL,\n", + " SpatialDataWrapper,\n", + " get_initial_coordination_scope_prefix\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(\n", + " schema_version=\"1.0.18\",\n", + " name='Visium SpatialData Demo (blobs)',\n", + ")\n", + "# Add data to the configuration:\n", + "wrapper = SpatialDataWrapper(\n", + " sdata_store=spatialdata_filepath,\n", + " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", + " image_path=\"images/blobs_image\",\n", + " obs_segmentations_path=\"labels/blobs_labels\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"obsType\": \"blob\",\n", + " \"fileUid\": \"my_unique_id\"\n", + " }\n", + ")\n", + "dataset = vc.add_dataset(name='Blobs').add_object(wrapper)\n", + "\n", + "# Add views (visualizations) to the configuration:\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'imageLayer': CL([{\n", + " \"fileUid\": \"my_unique_id\",\n", + " 'photometricInterpretation': 'BlackIsZero',\n", + " 'spatialLayerOpacity': 0.9,\n", + " 'imageChannel': CL([\n", + " {\n", + " \"spatialTargetC\": 0,\n", + " \"spatialChannelColor\": [255, 0, 0],\n", + " \"spatialChannelOpacity\": 1.0\n", + " },\n", + " {\n", + " \"spatialTargetC\": 1,\n", + " \"spatialChannelColor\": [0, 255, 0],\n", + " \"spatialChannelOpacity\": 1.0\n", + " },\n", + " {\n", + " \"spatialTargetC\": 2,\n", + " \"spatialChannelColor\": [0, 0, 255],\n", + " \"spatialChannelOpacity\": 1.0\n", + " }\n", + " ])\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", + "\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'segmentationLayer': CL([{\n", + " \"fileUid\": \"my_unique_id\",\n", + " 'segmentationChannel': CL([{\n", + " 'spatialChannelVisible': True,\n", + " 'obsType': 'blob',\n", + " }]),\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", + "\n", + "# Layout the views\n", + "vc.layout(spatial | layer_controller);" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Render the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/spatial_data_kpmp_vis.ipynb b/docs/notebooks/__ipynb__/spatial_data_kpmp_vis.ipynb new file mode 100644 index 00000000..a4f871b4 --- /dev/null +++ b/docs/notebooks/__ipynb__/spatial_data_kpmp_vis.ipynb @@ -0,0 +1,481 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "# Reference: https://github.com/vitessce/vitessce/blob/main/examples/configs/src/view-configs/spatial-beta/kpmp.js" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " CoordinationLevel as CL,\n", + " SpatialDataWrapper,\n", + " AnnDataWrapper,\n", + " ImageOmeTiffWrapper,\n", + " ImageOmeZarrWrapper,\n", + " ObsSegmentationsOmeZarrWrapper,\n", + " get_initial_coordination_scope_prefix,\n", + " hconcat,\n", + " vconcat,\n", + ")\n", + "from os.path import join" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "sdata_url = \"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/sdata.zarr\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a VitessceConfig instance.\n", + "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"SpatialData\")\n", + "\n", + "t_obstype = \"Tubule\"\n", + "a_obstype = \"Artery\"\n", + "ci_obstype = \"Cortical Interstitium\"\n", + "gsg_obstype = \"G. S. Glomerulus\"\n", + "ngsg_obstype = \"Non-G. S. Glomerulus\"\n", + "ifta_obstype = \"Interstitial Fibrosis and Tubular Atrophy\"\n", + "ptc_obstype = \"Peritubular Capillaries\"\n", + "\n", + "# Add a new dataset to the Vitessce configuration,\n", + "# then add the wrapper class instance to this dataset.\n", + "dataset = vc.add_dataset(name='KPMP').add_object(\n", + " SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " image_path=\"images/image\",\n", + " coordinate_system=\"global\",\n", + " )\n", + ").add_object(\n", + " SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " table_path=\"tables/table_tubules\",\n", + " obs_segmentations_path=\"labels/labels_tubules\",\n", + " obs_feature_matrix_path=\"tables/table_tubules/X\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"fileUid\": \"labels_tubules\",\n", + " \"obsType\": t_obstype,\n", + " \"featureType\": 'feature',\n", + " \"featureValueType\": 'value',\n", + " }\n", + " )\n", + ").add_object(\n", + " SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " obs_segmentations_path=\"labels/labels_arteries_arterioles\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"fileUid\": \"labels_arteries_arterioles\",\n", + " \"obsType\": a_obstype,\n", + " \"featureType\": 'feature',\n", + " \"featureValueType\": 'value',\n", + " }\n", + " )\n", + ").add_object(\n", + " SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " table_path=\"tables/table_cortical_interstitia\",\n", + " obs_segmentations_path=\"labels/labels_cortical_interstitia\",\n", + " obs_feature_matrix_path=\"tables/table_cortical_interstitia/X\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"fileUid\": \"labels_cortical_interstitia\",\n", + " \"obsType\": ci_obstype,\n", + " \"featureType\": 'feature',\n", + " \"featureValueType\": 'value',\n", + " }\n", + " )\n", + ").add_object(\n", + " SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " table_path=\"tables/table_globally_sclerotic_glomeruli\",\n", + " obs_segmentations_path=\"labels/labels_globally_sclerotic_glomeruli\",\n", + " obs_feature_matrix_path=\"tables/table_globally_sclerotic_glomeruli/X\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"fileUid\": \"labels_globally_sclerotic_glomeruli\",\n", + " \"obsType\": gsg_obstype,\n", + " \"featureType\": 'feature',\n", + " \"featureValueType\": 'value',\n", + " }\n", + " )\n", + ").add_object(\n", + " SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " table_path=\"tables/table_non_globally_sclerotic_glomeruli\",\n", + " obs_segmentations_path=\"labels/labels_non_globally_sclerotic_glomeruli\",\n", + " obs_feature_matrix_path=\"tables/table_non_globally_sclerotic_glomeruli/X\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"fileUid\": \"labels_non_globally_sclerotic_glomeruli\",\n", + " \"obsType\": ngsg_obstype,\n", + " \"featureType\": 'feature',\n", + " \"featureValueType\": 'value',\n", + " }\n", + " )\n", + ").add_object(\n", + " SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " table_path=\"tables/table_interstitialfibrosis_and_tubular_atrophy\",\n", + " obs_segmentations_path=\"labels/labels_interstitialfibrosis_and_tubular_atrophy\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"fileUid\": \"labels_interstitialfibrosis_and_tubular_atrophy\",\n", + " \"obsType\": ifta_obstype,\n", + " \"featureType\": 'feature',\n", + " \"featureValueType\": 'value',\n", + " }\n", + " )\n", + ").add_object(\n", + " SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " table_path=\"tables/table_peritubular_capillaries\",\n", + " obs_segmentations_path=\"labels/labels_peritubular_capillaries\",\n", + " obs_feature_matrix_path=\"tables/table_peritubular_capillaries/X\",\n", + " obs_set_paths=[\n", + " \"tables/table_peritubular_capillaries/obs/cortex_ifta_set\",\n", + " \"tables/table_peritubular_capillaries/obs/cortex_set\",\n", + " \"tables/table_peritubular_capillaries/obs/ifta_set\",\n", + " [\"tables/table_peritubular_capillaries/obs/cortex_set\", \"tables/table_peritubular_capillaries/obs/ifta_set\"],\n", + " ],\n", + " obs_set_names=[\n", + " \"Cortex and IFTA membership\",\n", + " \"Cortex membership\",\n", + " \"IFTA membership\",\n", + " \"Cortex and IFTA hierarchy\",\n", + " ],\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"fileUid\": \"labels_peritubular_capillaries\",\n", + " \"obsType\": ptc_obstype,\n", + " \"featureType\": 'feature',\n", + " \"featureValueType\": 'value',\n", + " }\n", + " )\n", + ")\n", + "\n", + "# Add views (visualizations) to the configuration.\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "# Tubules\n", + "tubules_feature_list = vc.add_view(\"featureList\", dataset=dataset).set_props(title=\"Tubules\")\n", + "tubules_histogram = vc.add_view(\"featureValueHistogram\", dataset=dataset)\n", + "# Peritubular capillaries\n", + "pt_feature_list = vc.add_view(\"featureList\", dataset=dataset).set_props(title=\"Peritubular Capillaries\")\n", + "pt_histogram = vc.add_view(\"featureValueHistogram\", dataset=dataset)\n", + "# GSG\n", + "gsg_feature_list = vc.add_view(\"featureList\", dataset=dataset).set_props(title=\"Globally Sclerotic Glomeruli\")\n", + "gsg_histogram = vc.add_view(\"featureValueHistogram\", dataset=dataset)\n", + "# NGSG\n", + "ngsg_feature_list = vc.add_view(\"featureList\", dataset=dataset).set_props(title=\"Non-Globally Sclerotic Glomeruli\")\n", + "ngsg_histogram = vc.add_view(\"featureValueHistogram\", dataset=dataset)\n", + "\n", + "# Add obsSets, obsSetSizes, and violin plot views for PTC sets+areas/aspectRatio\n", + "pt_sets = vc.add_view(\"obsSets\", dataset=dataset)\n", + "pt_bar_plot = vc.add_view(\"obsSetSizes\", dataset=dataset)\n", + "pt_violin_plot = vc.add_view(\"obsSetFeatureValueDistribution\", dataset=dataset).set_props(jitter=True)\n", + "\n", + "\n", + "# Coordination of views.\n", + "[ft_scope, fvt_scope] = vc.add_coordination(\"featureType\", \"featureValueType\")\n", + "ft_scope.set_value(\"feature\")\n", + "fvt_scope.set_value(\"value\")\n", + "\n", + "[t_ot_scope, t_fs_scope, t_oce_scope] = vc.add_coordination(\"obsType\", \"featureSelection\", \"obsColorEncoding\")\n", + "t_ot_scope.set_value(t_obstype)\n", + "t_oce_scope.set_value(\"spatialChannelColor\")\n", + "\n", + "[pt_ot_scope, pt_fs_scope, pt_oce_scope] = vc.add_coordination(\"obsType\", \"featureSelection\", \"obsColorEncoding\")\n", + "pt_ot_scope.set_value(ptc_obstype)\n", + "pt_fs_scope.set_value([\"Area\"])\n", + "pt_oce_scope.set_value(\"cellSetSelection\")\n", + "\n", + "\n", + "[gsg_ot_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope] = vc.add_coordination(\"obsType\", \"featureSelection\", \"obsColorEncoding\", \"featureValueColormapRange\")\n", + "gsg_ot_scope.set_value(gsg_obstype)\n", + "gsg_oce_scope.set_value(\"spatialChannelColor\")\n", + "gsg_fvcr_scope.set_value([(3077 - 2333) / (29911 - 2333), 1.0])\n", + "\n", + "[ngsg_ot_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope] = vc.add_coordination(\"obsType\", \"featureSelection\", \"obsColorEncoding\", \"featureValueColormapRange\")\n", + "ngsg_ot_scope.set_value(ngsg_obstype)\n", + "ngsg_oce_scope.set_value(\"spatialChannelColor\")\n", + "ngsg_fvcr_scope.set_value([0.0, 1 - (59451 - 29911) / (59451 - 3077)])\n", + "\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " \"imageLayer\": CL([{\n", + " \"spatialLayerOpacity\": 0.1,\n", + " \"photometricInterpretation\": \"RGB\",\n", + " }]),\n", + "}, meta=True, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", + "\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " \"segmentationLayer\": CL([\n", + " {\n", + " \"fileUid\": \"labels_tubules\",\n", + " \"segmentationChannel\": CL([{\n", + " \"spatialTargetC\": 0,\n", + " \"obsType\": t_ot_scope,\n", + " \"featureType\": ft_scope,\n", + " \"featureValueType\": fvt_scope,\n", + " \"featureSelection\": t_fs_scope,\n", + " \"spatialChannelVisible\": False,\n", + " \"spatialChannelColor\": [73, 155, 119],\n", + " \"spatialChannelOpacity\": 0.5,\n", + " \"obsColorEncoding\": t_oce_scope,\n", + " \"featureValueColormapRange\": [0, 1],\n", + " \"featureAggregationStrategy\": \"first\",\n", + " \"spatialSegmentationFilled\": True,\n", + " \"obsHighlight\": None,\n", + " }]),\n", + " },\n", + " {\n", + " \"fileUid\": \"labels_arteries_arterioles\",\n", + " \"segmentationChannel\": CL([{\n", + " \"spatialTargetC\": 0,\n", + " \"obsType\": \"Artery\",\n", + " \"featureType\": ft_scope,\n", + " \"featureValueType\": fvt_scope,\n", + " \"spatialChannelVisible\": False,\n", + " \"spatialChannelColor\": [237, 226, 107],\n", + " \"spatialChannelOpacity\": 0.5,\n", + " \"obsColorEncoding\": \"spatialChannelColor\",\n", + " \"featureValueColormapRange\": [0, 1],\n", + " \"featureAggregationStrategy\": \"first\",\n", + " \"spatialSegmentationFilled\": True,\n", + " \"obsHighlight\": None,\n", + " }]),\n", + " },\n", + " {\n", + " \"fileUid\": \"labels_cortical_interstitia\",\n", + " \"segmentationChannel\": CL([{\n", + " \"spatialTargetC\": 0,\n", + " \"obsType\": \"Cortical Interstitium\",\n", + " \"featureType\": ft_scope,\n", + " \"featureValueType\": fvt_scope,\n", + " \"spatialChannelVisible\": False,\n", + " \"spatialChannelColor\": [255, 255, 255],\n", + " \"spatialChannelOpacity\": 0.5,\n", + " \"obsColorEncoding\": \"spatialChannelColor\",\n", + " \"featureValueColormapRange\": [0, 1],\n", + " \"featureAggregationStrategy\": \"first\",\n", + " \"spatialSegmentationFilled\": True,\n", + " \"obsHighlight\": None,\n", + " }]),\n", + " },\n", + " {\n", + " \"fileUid\": \"labels_globally_sclerotic_glomeruli\",\n", + " \"segmentationChannel\": CL([{\n", + " \"spatialTargetC\": 0,\n", + " \"obsType\": gsg_ot_scope,\n", + " \"featureType\": ft_scope,\n", + " \"featureValueType\": fvt_scope,\n", + " \"featureSelection\": gsg_fs_scope,\n", + " \"spatialChannelVisible\": False,\n", + " \"spatialChannelColor\": [52, 113, 171],\n", + " \"spatialChannelOpacity\": 0.5,\n", + " \"obsColorEncoding\": gsg_oce_scope,\n", + " \"featureValueColormapRange\": gsg_fvcr_scope,\n", + " \"featureAggregationStrategy\": \"first\",\n", + " \"spatialSegmentationFilled\": True,\n", + " \"obsHighlight\": None,\n", + " }]),\n", + " },\n", + " {\n", + " \"fileUid\": \"labels_non_globally_sclerotic_glomeruli\",\n", + " \"segmentationChannel\": CL([{\n", + " \"spatialTargetC\": 0,\n", + " \"obsType\": ngsg_ot_scope,\n", + " \"featureType\": ft_scope,\n", + " \"featureValueType\": fvt_scope,\n", + " \"featureSelection\": ngsg_fs_scope,\n", + " \"spatialChannelVisible\": False,\n", + " \"spatialChannelColor\": [114, 179, 226],\n", + " \"spatialChannelOpacity\": 0.5,\n", + " \"obsColorEncoding\": ngsg_oce_scope,\n", + " \"featureValueColormapRange\": ngsg_fvcr_scope,\n", + " \"featureAggregationStrategy\": \"first\",\n", + " \"spatialSegmentationFilled\": True,\n", + " \"obsHighlight\": None,\n", + " }]),\n", + " },\n", + " {\n", + " \"fileUid\": \"labels_interstitialfibrosis_and_tubular_atrophy\",\n", + " \"segmentationChannel\": CL([{\n", + " \"spatialTargetC\": 0,\n", + " \"obsType\": \"Interstitial Fibrosis and Tubular Atrophy\",\n", + " \"featureType\": ft_scope,\n", + " \"featureValueType\": fvt_scope,\n", + " \"spatialChannelVisible\": True,\n", + " \"spatialChannelColor\": [218, 161, 66],\n", + " \"spatialChannelOpacity\": 1.0,\n", + " \"obsColorEncoding\": \"spatialChannelColor\",\n", + " \"featureValueColormapRange\": [0, 1],\n", + " \"featureAggregationStrategy\": \"first\",\n", + " \"spatialSegmentationFilled\": False,\n", + " \"obsHighlight\": None,\n", + " }]),\n", + " },\n", + " {\n", + " \"fileUid\": \"labels_peritubular_capillaries\",\n", + " \"segmentationChannel\": CL([{\n", + " \"spatialTargetC\": 0,\n", + " \"obsType\": pt_ot_scope,\n", + " \"featureType\": ft_scope,\n", + " \"featureValueType\": fvt_scope,\n", + " \"featureSelection\": pt_fs_scope,\n", + " \"spatialChannelVisible\": True,\n", + " \"spatialChannelColor\": [197, 101, 47],\n", + " \"spatialChannelOpacity\": 1.0,\n", + " \"obsColorEncoding\": pt_oce_scope,\n", + " \"featureValueColormapRange\": [0, 0.5],\n", + " \"featureAggregationStrategy\": \"first\",\n", + " \"spatialSegmentationFilled\": True,\n", + " \"obsHighlight\": None,\n", + " }]),\n", + " }\n", + " ]),\n", + "}, meta=True, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", + "\n", + "tubules_feature_list.use_coordination(t_ot_scope, ft_scope, fvt_scope, t_fs_scope, t_oce_scope)\n", + "tubules_histogram.use_coordination(t_ot_scope, ft_scope, fvt_scope, t_fs_scope, t_oce_scope)\n", + "\n", + "pt_feature_list.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", + "pt_histogram.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", + "\n", + "pt_sets.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", + "pt_bar_plot.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", + "pt_violin_plot.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", + "\n", + "\n", + "gsg_feature_list.use_coordination(gsg_ot_scope, ft_scope, fvt_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope)\n", + "gsg_histogram.use_coordination(gsg_ot_scope, ft_scope, fvt_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope)\n", + "\n", + "ngsg_feature_list.use_coordination(ngsg_ot_scope, ft_scope, fvt_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope)\n", + "ngsg_histogram.use_coordination(ngsg_ot_scope, ft_scope, fvt_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope)\n", + "\n", + "\n", + "# Layout the views in a grid arrangement.\n", + "vc.layout(vconcat(\n", + " hconcat(spatial, layer_controller, split=[3, 1]),\n", + " hconcat(\n", + " (tubules_feature_list / tubules_histogram),\n", + " (pt_feature_list / pt_histogram),\n", + " (gsg_feature_list / gsg_histogram),\n", + " (ngsg_feature_list / ngsg_histogram)\n", + " ),\n", + " hconcat(pt_sets, pt_bar_plot, pt_violin_plot)\n", + "));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "SFPL", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget(height=1000)\n", + "vw" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "#import json\n", + "#print(json.dumps(vc.to_dict(), indent=2))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/spatial_data_merfish.ipynb b/docs/notebooks/__ipynb__/spatial_data_merfish.ipynb new file mode 100644 index 00000000..822f44c8 --- /dev/null +++ b/docs/notebooks/__ipynb__/spatial_data_merfish.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a SpatialData object, blobs example" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "sdata_url = \"https://data-2.vitessce.io/data/moffitt/merfish_mouse_ileum.sdata.zarr\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " CoordinationLevel as CL,\n", + " SpatialDataWrapper,\n", + " get_initial_coordination_scope_prefix\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(\n", + " schema_version=\"1.0.18\",\n", + " name='SpatialData with MERFISH data',\n", + ")\n", + "# Add data to the configuration:\n", + "\n", + "dataset = vc.add_dataset(name='MERFISH').add_object(SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", + " image_path=\"images/stains\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"fileUid\": \"stains\"\n", + " }\n", + ")).add_object(SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", + " obs_segmentations_path=\"labels/dapi_labels\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"obsType\": \"nucleus\",\n", + " \"fileUid\": \"dapi\"\n", + " }\n", + ")).add_object(SpatialDataWrapper(\n", + " sdata_url=sdata_url,\n", + " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", + " obs_segmentations_path=\"labels/membrane_labels\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"obsType\": \"cell\",\n", + " \"fileUid\": \"membrane\"\n", + " }\n", + "))\n", + "\n", + "# Add views (visualizations) to the configuration:\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'imageLayer': CL([{\n", + " \"fileUid\": \"stains\",\n", + " 'photometricInterpretation': 'BlackIsZero',\n", + " 'spatialLayerOpacity': 1.0,\n", + " 'spatialLayerVisible': True,\n", + " 'imageChannel': CL([\n", + " {\n", + " 'spatialChannelVisible': True,\n", + " \"spatialTargetC\": 0, # DAPI, Nucleus\n", + " \"spatialChannelColor\": [0, 0, 255],\n", + " \"spatialChannelOpacity\": 1.0\n", + " },\n", + " {\n", + " 'spatialChannelVisible': True,\n", + " \"spatialTargetC\": 1, # Membrane, Cell\n", + " \"spatialChannelColor\": [255, 255, 255],\n", + " \"spatialChannelOpacity\": 1.0\n", + " }\n", + " ])\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", + "\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'segmentationLayer': CL([{\n", + " \"fileUid\": \"membrane\",\n", + " 'spatialLayerOpacity': 1.0,\n", + " 'spatialLayerVisible': True,\n", + " 'segmentationChannel': CL([{\n", + " 'spatialChannelVisible': True,\n", + " 'obsType': 'cell',\n", + " \"spatialChannelColor\": [200, 200, 200],\n", + " \"obsColorEncoding\": \"spatialChannelColor\",\n", + " }]),\n", + " }, {\n", + " \"fileUid\": \"dapi\",\n", + " 'spatialLayerOpacity': 1.0,\n", + " 'spatialLayerVisible': True,\n", + " 'segmentationChannel': CL([{\n", + " 'spatialChannelVisible': True,\n", + " 'obsType': 'nucleus',\n", + " \"spatialChannelColor\": [255, 255, 255],\n", + " \"obsColorEncoding\": \"spatialChannelColor\",\n", + " }]),\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", + "\n", + "# Layout the views\n", + "vc.layout(spatial | layer_controller);" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Render the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "SFPL", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/spatial_data_merfish_2.ipynb b/docs/notebooks/__ipynb__/spatial_data_merfish_2.ipynb new file mode 100644 index 00000000..861bcdf4 --- /dev/null +++ b/docs/notebooks/__ipynb__/spatial_data_merfish_2.ipynb @@ -0,0 +1,189 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "import zipfile\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " CoordinationLevel as CL,\n", + " SpatialDataWrapper,\n", + " get_initial_coordination_scope_prefix\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "data_dir = \"data\"\n", + "zip_filepath = join(data_dir, \"merfish_2.spatialdata.zarr.zip\")\n", + "spatialdata_filepath = join(data_dir, \"merfish_2.spatialdata.zarr\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "if not isdir(spatialdata_filepath):\n", + " if not isfile(zip_filepath):\n", + " os.makedirs(data_dir, exist_ok=True)\n", + " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/merfish.zip', zip_filepath)\n", + " with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n", + " zip_ref.extractall(data_dir)\n", + " os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(\n", + " schema_version=\"1.0.18\",\n", + " name='MERFISH SpatialData Demo',\n", + ")\n", + "# Add data to the configuration:\n", + "wrapper = SpatialDataWrapper(\n", + " sdata_path=spatialdata_filepath,\n", + " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", + " image_path=\"images/rasterized\",\n", + " table_path=\"tables/table\",\n", + " obs_feature_matrix_path=\"tables/table/X\",\n", + " obs_spots_path=\"shapes/cells\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " # The following tells Vitessce to consider each observation as a \"spot\"\n", + " \"obsType\": \"cell\",\n", + " }\n", + ")\n", + "dataset = vc.add_dataset(name='MERFISH').add_object(wrapper)\n", + "\n", + "# Add views (visualizations) to the configuration:\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "feature_list = vc.add_view(\"featureList\", dataset=dataset)\n", + "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "obs_sets = vc.add_view(\"obsSets\", dataset=dataset)\n", + "\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'spotLayer': CL([{\n", + " 'obsType': 'cell',\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSpots\"))\n", + "\n", + "vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], [wrapper.obs_type_label])\n", + "\n", + "# Layout the views\n", + "vc.layout(spatial | (feature_list / layer_controller / obs_sets));" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Render the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/spatial_data_mouseliver.ipynb b/docs/notebooks/__ipynb__/spatial_data_mouseliver.ipynb new file mode 100644 index 00000000..c8ca14a2 --- /dev/null +++ b/docs/notebooks/__ipynb__/spatial_data_mouseliver.ipynb @@ -0,0 +1,898 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a SpatialData object and individual Spatial Elements, local data" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "This notebook explains how to create interactive visualizations of data that is accessible locally.\n", + "\n", + "\n", + "We progress through different visualization tasks, first demonstrating how Vitessce facilitates integrated imaging and spatial single-cell visualizations, then demonstrating visualization of non-spatial and image-only datasets." + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Utility dependencies" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "First, we import utility dependencies which will be used to download the example dataset and manipulate file paths, zip files, and JSON files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "import zipfile\n", + "import json" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Dependencies for Vitessce" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Here, we import classes and functions from the `vitessce` Python package.\n", + "This package includes not only APIs for [visualization configuration](https://python-docs.vitessce.io/api_config.html) but also [helper functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) for basic data transformation tasks.\n", + "To specify mappings between data fields and visualization properties, the package contains [classes](https://python-docs.vitessce.io/api_data.html#module-vitessce.wrappers) which wrap standard single-cell data structures stored in formats including [AnnData](https://doi.org/10.1101/2021.12.16.473007), [SpatialData](https://doi.org/10.1038/s41592-024-02212-x), [OME-TIFF](https://doi.org/10.1007/978-3-030-23937-4_1), and [OME-Zarr](https://doi.org/10.1038/s41592-021-01326-w):\n", + "\n", + "- [AnnDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.AnnDataWrapper)\n", + "- [ImageOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeTiffWrapper)\n", + "- [ImageOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeZarrWrapper)\n", + "- [ObsSegmentationsOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeTiffWrapper)\n", + "- [ObsSegmentationsOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeZarrWrapper)\n", + "- [SpatialDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.SpatialDataWrapper)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " CoordinationLevel as CL,\n", + " SpatialDataWrapper,\n", + " AnnDataWrapper,\n", + " ImageOmeTiffWrapper,\n", + " ImageOmeZarrWrapper,\n", + " ObsSegmentationsOmeZarrWrapper,\n", + " get_initial_coordination_scope_prefix,\n", + " hconcat,\n", + " vconcat,\n", + ")\n", + "from vitessce.data_utils import (\n", + " VAR_CHUNK_SIZE,\n", + " generate_h5ad_ref_spec,\n", + " multiplex_img_to_ome_tiff,\n", + " multiplex_img_to_ome_zarr,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Dependencies for data structures" + ] + }, + { + "cell_type": "markdown", + "id": "Kclp", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "In this blog post, we perform basic data transformation tasks to save individual elements of an integrated SpatialData object to separate files in AnnData, OME-TIFF, and OME-Zarr formats.\n", + "To perform these data transformations, we import the following dependencies.\n", + "In general, you will typically not need to import all of these dependencies, either because you are only working with data in one of these formats, or because the data you intend to visualize is already saved to a file or directory.\n", + "\n", + "Note: Dependencies such as `spatialdata` may need to be installed before they can be imported in the next code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "emfo", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from spatialdata import read_zarr\n", + "from anndata import AnnData\n", + "from ome_zarr.writer import write_image\n", + "import tifffile\n", + "from generate_tiff_offsets import get_offsets" + ] + }, + { + "cell_type": "markdown", + "id": "Hstk", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Download example dataset" + ] + }, + { + "cell_type": "markdown", + "id": "nWHF", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "We download a mouse liver dataset which serves as a SpatialData [example dataset](https://github.com/scverse/spatialdata-notebooks/blob/main/notebooks/examples/transformations.ipynb).\n", + "\n", + "This dataset was generated by [Guilliams et al.](https://doi.org/10.1016/j.cell.2021.12.018) and processed using [SPArrOW](https://doi.org/10.1101/2024.07.04.601829) during the SpatialData [developer workshop](https://doi.org/10.37044/osf.io/8ck3e) in 2024." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "iLit", + "metadata": {}, + "outputs": [], + "source": [ + "data_dir = \"data\"\n", + "zip_filepath = join(data_dir, \"mouse_liver.spatialdata.zarr.zip\")\n", + "spatialdata_filepath = join(data_dir, \"mouse_liver.spatialdata.zarr\")\n", + "adata_zarr_filepath = join(data_dir, \"mouse_liver.anndata.zarr\")\n", + "adata_h5ad_filepath = join(data_dir, \"mouse_liver.h5ad\")\n", + "ref_spec_json_filepath = join(data_dir, \"mouse_liver.h5ad.ref.json\")\n", + "ome_tiff_filepath = join(data_dir, \"mouse_liver.ome.tif\")\n", + "offsets_json_filepath = join(data_dir, \"mouse_liver.ome.tif.offsets.json\")\n", + "ome_zarr_filepath = join(data_dir, \"mouse_liver.ome.zarr\")\n", + "labels_ome_zarr_filepath = join(data_dir, \"mouse_liver.labels.ome.zarr\")" + ] + }, + { + "cell_type": "markdown", + "id": "ZHCJ", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "The following code uses Python's `urlretrieve` to download the SpatialData object as a zip file, then unzips the file using the `zipfile` module." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ROlb", + "metadata": {}, + "outputs": [], + "source": [ + "if not isdir(spatialdata_filepath):\n", + " if not isfile(zip_filepath):\n", + " os.makedirs(data_dir, exist_ok=True)\n", + " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/mouse_liver.zip', zip_filepath)\n", + " with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n", + " zip_ref.extractall(data_dir)\n", + " os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "qnkX", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Visualization of a SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "TqIu", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "SpatialData objects are the most complex type of data structure we will work with in this blog post.\n", + "SpatialData objects function as contains for multiple types of Spatial Elements:\n", + "\n", + "- Tables (each table is represented as an AnnData object)\n", + "- Points (e.g., coordinates of transcripts from FISH-based experiments)\n", + "- Shapes (vector-based shapes such as polygons and circles)\n", + "- Labels (label images, i.e., segmentation bitmasks; each label image is stored using OME-Zarr)\n", + "- Images (microscopy images; each image is stored using OME-Zarr)" + ] + }, + { + "cell_type": "markdown", + "id": "Vxnm", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views.\n", + "To visualize data stored in a SpatialData object, we use the `SpatialDataWrapper` class and specify the paths (relative to the root of the Zarr [directory store](https://zarr.readthedocs.io/en/v2.18.5/api/storage.html#zarr.storage.DirectoryStore)) to different spatial elements of interest." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "DnEU", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version='1.0.18', name='SpatialData Demo')\n", + "_wrapper = SpatialDataWrapper(sdata_path=spatialdata_filepath, table_path='tables/table', image_path='images/raw_image', obs_segmentations_path='labels/segmentation_mask', obs_feature_matrix_path='tables/table/X', obs_set_paths=['tables/table/obs/annotation'], obs_set_names=['Annotation'], region='nucleus_boundaries', coordinate_system='global', coordination_values={'obsType': 'cell'})\n", + "_dataset = vc.add_dataset(name='Mouse Liver').add_object(_wrapper)\n", + "_spatial = vc.add_view('spatialBeta', dataset=_dataset)\n", + "feature_list = vc.add_view('featureList', dataset=_dataset)\n", + "_layer_controller = vc.add_view('layerControllerBeta', dataset=_dataset)\n", + "obs_sets = vc.add_view('obsSets', dataset=_dataset)\n", + "_heatmap = vc.add_view('heatmap', dataset=_dataset)\n", + "[obs_color_encoding_scope] = vc.add_coordination('obsColorEncoding')\n", + "obs_color_encoding_scope.set_value('cellSetSelection')\n", + "vc.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image'))\n", + "vc.link_views_by_dict([_spatial, _layer_controller], {'segmentationLayer': CL([{'segmentationChannel': CL([{'obsColorEncoding': obs_color_encoding_scope}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'obsSegmentations'))\n", + "vc.link_views([_spatial, _layer_controller, feature_list, obs_sets, _heatmap], ['obsType'], [_wrapper.obs_type_label])\n", + "vc.link_views_by_dict([feature_list, obs_sets, _heatmap], {'obsColorEncoding': obs_color_encoding_scope}, meta=False)\n", + "vc.layout(_spatial / _heatmap | _layer_controller / (feature_list | obs_sets))" + ] + }, + { + "cell_type": "markdown", + "id": "ulZA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Render the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecfG", + "metadata": {}, + "outputs": [], + "source": [ + "_vw = vc.widget()\n", + "_vw" + ] + }, + { + "cell_type": "markdown", + "id": "Pvdt", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Extract AnnData object from SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "ZBYS", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "The above example demonstrates how to visualize a spatial 'omics dataset containing not only single-cell information (e.g., a cell-by-gene expression matrix, cell type annotations) but also an image and cell segmentations.\n", + "To demonstrate how to use Vitessce to visualize data from a (non-spatial) single-cell experiment, we will extract this information from the SpatialData object and save it to a simpler [AnnData](https://anndata.readthedocs.io/) object (ignoring the imaging and spatially-resolved elements)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aLJB", + "metadata": {}, + "outputs": [], + "source": [ + "sdata = read_zarr(spatialdata_filepath)\n", + "sdata" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "nHfw", + "metadata": {}, + "outputs": [], + "source": [ + "adata = sdata.tables['table']\n", + "adata" + ] + }, + { + "cell_type": "markdown", + "id": "xXTn", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "As Zarr-formatted data can be easily visualized by Vitessce, we recommend saving the AnnData object to a Zarr store using the [write_zarr](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_zarr.html) method.\n", + "Optionally, the shape of array chunks (for the AnnData `X` array) can be specified as a parameter, to optimize performance based on data access patterns.\n", + "For example, a common pattern is to visualize data across all cells for one gene.\n", + "To support such a pattern, the chunk shape can be specified as follows, `(total number of cells, small number of genes)`, resulting in tall-and-skinny array chunks." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "AjVT", + "metadata": {}, + "outputs": [], + "source": [ + "adata.write_zarr(adata_zarr_filepath, chunks=(adata.shape[0], VAR_CHUNK_SIZE))" + ] + }, + { + "cell_type": "markdown", + "id": "pHFh", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Alternatively, your AnnData object may already be stored using the H5AD (HDF5-based) format.\n", + "To demonstrate this scenario, we save the object using the [write_h5ad](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_h5ad.html) method." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "NCOB", + "metadata": {}, + "outputs": [], + "source": [ + "adata.write_h5ad(adata_h5ad_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "aqbW", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "To read H5AD-formatted data, Vitessce requires an accompanying JSON [references specification](https://fsspec.github.io/kerchunk/spec.html) file, which can be constructed using the `generate_h5ad_ref_spec` utility function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "TRpd", + "metadata": {}, + "outputs": [], + "source": [ + "ref_dict = generate_h5ad_ref_spec(adata_h5ad_filepath)\n", + "with open(ref_spec_json_filepath, 'w') as _f:\n", + " json.dump(ref_dict, _f)" + ] + }, + { + "cell_type": "markdown", + "id": "TXez", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Visualization of an AnnData object" + ] + }, + { + "cell_type": "markdown", + "id": "dNNg", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Zarr-based AnnData" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "yCnT", + "metadata": {}, + "outputs": [], + "source": [ + "vc_1 = VitessceConfig(schema_version='1.0.18', name='AnnData (zarr)')\n", + "_wrapper = AnnDataWrapper(adata_path=adata_zarr_filepath, obs_feature_matrix_path='X', obs_set_paths=['obs/annotation'], obs_set_names=['Annotation'], coordination_values={'obsType': 'cell'})\n", + "_dataset = vc_1.add_dataset(name='Mouse Liver').add_object(_wrapper)\n", + "_heatmap = vc_1.add_view(vt.HEATMAP, dataset=_dataset)\n", + "feature_list_1 = vc_1.add_view(vt.FEATURE_LIST, dataset=_dataset)\n", + "obs_sets_1 = vc_1.add_view(vt.OBS_SETS, dataset=_dataset)\n", + "_violin_plots = vc_1.add_view('obsSetFeatureValueDistribution', dataset=_dataset)\n", + "vc_1.link_views([_heatmap, feature_list_1, obs_sets_1], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]])\n", + "vc_1.layout(_heatmap / _violin_plots | feature_list_1 / obs_sets_1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "wlCL", + "metadata": {}, + "outputs": [], + "source": [ + "vc_1.widget()" + ] + }, + { + "cell_type": "markdown", + "id": "kqZH", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### H5AD-based AnnData" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "wAgl", + "metadata": {}, + "outputs": [], + "source": [ + "vc_2 = VitessceConfig(schema_version='1.0.18', name='AnnData (h5ad)')\n", + "_wrapper = AnnDataWrapper(adata_path=adata_h5ad_filepath, ref_path=ref_spec_json_filepath, obs_feature_matrix_path='X', obs_set_paths=['obs/annotation'], obs_set_names=['Annotation'], coordination_values={'obsType': 'cell'})\n", + "_dataset = vc_2.add_dataset(name='Mouse Liver').add_object(_wrapper)\n", + "_heatmap = vc_2.add_view(vt.HEATMAP, dataset=_dataset)\n", + "feature_list_2 = vc_2.add_view(vt.FEATURE_LIST, dataset=_dataset)\n", + "obs_sets_2 = vc_2.add_view(vt.OBS_SETS, dataset=_dataset)\n", + "_violin_plots = vc_2.add_view('obsSetFeatureValueDistribution', dataset=_dataset)\n", + "vc_2.link_views([_heatmap, feature_list_2, obs_sets_2], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]])\n", + "vc_2.layout(_heatmap / _violin_plots | feature_list_2 / obs_sets_2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "rEll", + "metadata": {}, + "outputs": [], + "source": [ + "vc_2.widget()" + ] + }, + { + "cell_type": "markdown", + "id": "dGlV", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Extract image from SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "SdmI", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "In contrast to extraction of the non-spatial data from the SpatialData object, we can extract the imaging (and segmentation/label image) data and save it to a dedicated bioimaging file format." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lgWD", + "metadata": {}, + "outputs": [], + "source": [ + "img_arr = sdata.images['raw_image'].to_numpy()\n", + "labels_arr = sdata.labels['segmentation_mask'].to_numpy()\n", + "labels_arr = labels_arr[np.newaxis, :]" + ] + }, + { + "cell_type": "markdown", + "id": "yOPj", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Save image to OME-Zarr" + ] + }, + { + "cell_type": "markdown", + "id": "fwwy", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "For small images, the data can be saved to OME-Zarr or OME-TIFF format using [utility functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) from the Vitessce package.\n", + "Larger images require generation of an [image pyramid](https://en.wikipedia.org/wiki/Pyramid_(image_processing)), which can be performed using tools from the OME ecosystem such as [bioformats2raw](https://github.com/glencoesoftware/bioformats2raw) and [raw2ometiff](https://github.com/glencoesoftware/raw2ometiff)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "LJZf", + "metadata": {}, + "outputs": [], + "source": [ + "multiplex_img_to_ome_zarr(img_arr, [\"Channel 0\"], ome_zarr_filepath)\n", + "multiplex_img_to_ome_zarr(labels_arr, [\"cell\"], labels_ome_zarr_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "urSm", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Save image and segmentations to OME-TIFF" + ] + }, + { + "cell_type": "markdown", + "id": "jxvo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "To efficiently visualize OME-TIFF data using Vitessce, a JSON-based offsets file can be constructed using [generate-tiff-offsets](https://github.com/hms-dbmi/generate-tiff-offsets).\n", + "This JSON file contains byte offsets into different partitions of the TIFF file, effectively resulting in an \"indexed TIFF\" which is described by [Manz et al. 2022](https://doi.org/10.1038/s41592-022-01482-7)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mWxS", + "metadata": {}, + "outputs": [], + "source": [ + "multiplex_img_to_ome_tiff(img_arr, ['Channel 0'], ome_tiff_filepath)\n", + "offsets = get_offsets(ome_tiff_filepath)\n", + "with open(offsets_json_filepath, 'w') as _f:\n", + " json.dump(offsets, _f)" + ] + }, + { + "cell_type": "markdown", + "id": "CcZR", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Visualization of an image file" + ] + }, + { + "cell_type": "markdown", + "id": "YWSi", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### OME-Zarr image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "zlud", + "metadata": {}, + "outputs": [], + "source": [ + "vc_3 = VitessceConfig(schema_version='1.0.18', name='Image (ome-zarr)')\n", + "img_wrapper = ImageOmeZarrWrapper(img_path=ome_zarr_filepath, coordination_values={'fileUid': 'image'})\n", + "segmentations_wrapper = ObsSegmentationsOmeZarrWrapper(img_path=labels_ome_zarr_filepath, coordination_values={'fileUid': 'segmentations'})\n", + "_dataset = vc_3.add_dataset(name='Mouse Liver').add_object(img_wrapper).add_object(segmentations_wrapper)\n", + "_spatial = vc_3.add_view('spatialBeta', dataset=_dataset)\n", + "_layer_controller = vc_3.add_view('layerControllerBeta', dataset=_dataset)\n", + "vc_3.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'fileUid': 'image', 'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image'))\n", + "vc_3.link_views_by_dict([_spatial, _layer_controller], {'segmentationLayer': CL([{'fileUid': 'segmentations', 'segmentationChannel': CL([{'obsColorEncoding': 'spatialChannelColor', 'spatialChannelColor': [0, 255, 0], 'spatialChannelOpacity': 0.75, 'spatialSegmentationFilled': False, 'spatialSegmentationStrokeWidth': 0.25}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'obsSegmentations'))\n", + "vc_3.link_views([_spatial, _layer_controller, feature_list_2, obs_sets_2], ['obsType'], ['cell'])\n", + "vc_3.layout(hconcat(_spatial, _layer_controller, split=(2, 1)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "tZnO", + "metadata": {}, + "outputs": [], + "source": [ + "_vw = vc_3.widget()\n", + "_vw" + ] + }, + { + "cell_type": "markdown", + "id": "xvXZ", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### OME-TIFF image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "CLip", + "metadata": {}, + "outputs": [], + "source": [ + "vc_4 = VitessceConfig(schema_version='1.0.18', name='Image and segmentations (ome-tiff)')\n", + "_wrapper = ImageOmeTiffWrapper(img_path=ome_tiff_filepath, offsets_path=offsets_json_filepath)\n", + "_dataset = vc_4.add_dataset(name='Mouse Liver').add_object(_wrapper)\n", + "_spatial = vc_4.add_view('spatialBeta', dataset=_dataset)\n", + "_layer_controller = vc_4.add_view('layerControllerBeta', dataset=_dataset)\n", + "vc_4.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image'))\n", + "vc_4.layout(hconcat(_spatial, _layer_controller, split=(2, 1)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "YECM", + "metadata": {}, + "outputs": [], + "source": [ + "vc_4.widget()" + ] + }, + { + "cell_type": "markdown", + "id": "cEAS", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Data location options\n", + "\n", + "Vitessce can visualize data [not only stored](https://python-docs.vitessce.io/data_options.html) locally (referenced using local file or directory paths) but also stored remotely (referenced using absolute URL paths).\n", + "Depending on whether the Python kernel running the Jupyter process is running locally versus remotely (e.g., on a cluster or cloud platform such as Google Colab), certain data locations may be challenging to access from the machine running the Jupyter notebook frontend (i.e., the machine on which the web browser used to view the notebook is installed).\n", + "\n", + "To provide data via one of these alternative mechanisms, use parameters with the following suffices when instantiating the data wrapper classes.\n", + "\n", + "- `_path`: Local file or directory\n", + "- `_url`: Remote file or directory\n", + "- `_store`: Zarr-store-accessible (for zarr-based formats)\n", + "- `_artifact`: Lamin artifact\n", + "\n", + "For example, `adata_path` can be exchanged for one of the following options.\n", + "\n", + "```diff\n", + "AnnDataWrapper(\n", + "- adata_path=\"./mouse_liver.spatialdata.zarr\",\n", + "+ adata_url=\"https://example.com/mouse_liver.spatialdata.zarr\", # Absolute URL\n", + "+ adata_store=\"./mouse_liver.spatialdata.zarr\", # String interpreted as root of DirectoryStore\n", + "+ adata_store=zarr.DirectoryStore(\"./mouse_liver.spatialdata.zarr\"), # Instance of zarr.storage\n", + "+ adata_artifact=adata_zarr_artifact, # Instance of ln.Artifact\n", + " ...\n", + "```\n", + "\n", + "Note that the `_store` options are only available for Zarr-based formats, such as AnnDataWrapper, SpatialDataWrapper, and ImageOmeZarrWrapper.\n", + "Further, when multiple files are required, such as both `adata_path` and `ref_path` (for the JSON reference specification file accompanying an H5AD file) or `img_path` and `offsets_path`, multiple parameter suffices may need to be changed." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/spatial_data_mouseliver_remote.ipynb b/docs/notebooks/__ipynb__/spatial_data_mouseliver_remote.ipynb new file mode 100644 index 00000000..184e962b --- /dev/null +++ b/docs/notebooks/__ipynb__/spatial_data_mouseliver_remote.ipynb @@ -0,0 +1,684 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Vitessce Widget Tutorial" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a SpatialData object and individual Spatial Elements, remote data" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "This notebook explains how to create interactive visualizations of data that are accessible remotely.\n", + "\n", + "\n", + "We progress through different visualization tasks, first demonstrating how Vitessce facilitates integrated imaging and spatial single-cell visualizations, then demonstrating visualization of non-spatial and image-only datasets." + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Utility dependencies" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "First, we import utility dependencies which will be used to download the example dataset and manipulate file paths, zip files, and JSON files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "import zipfile\n", + "import json" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Dependencies for Vitessce" + ] + }, + { + "cell_type": "markdown", + "id": "BYtC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Here, we import classes and functions from the `vitessce` Python package.\n", + "This package includes not only APIs for [visualization configuration](https://python-docs.vitessce.io/api_config.html) but also [helper functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) for basic data transformation tasks.\n", + "To specify mappings between data fields and visualization properties, the package contains [classes](https://python-docs.vitessce.io/api_data.html#module-vitessce.wrappers) which wrap standard single-cell data structures stored in formats including [AnnData](https://doi.org/10.1101/2021.12.16.473007), [SpatialData](https://doi.org/10.1038/s41592-024-02212-x), [OME-TIFF](https://doi.org/10.1007/978-3-030-23937-4_1), and [OME-Zarr](https://doi.org/10.1038/s41592-021-01326-w):\n", + "\n", + "- [AnnDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.AnnDataWrapper)\n", + "- [ImageOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeTiffWrapper)\n", + "- [ImageOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeZarrWrapper)\n", + "- [ObsSegmentationsOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeTiffWrapper)\n", + "- [ObsSegmentationsOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeZarrWrapper)\n", + "- [SpatialDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.SpatialDataWrapper)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "RGSE", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " CoordinationLevel as CL,\n", + " SpatialDataWrapper,\n", + " AnnDataWrapper,\n", + " ImageOmeTiffWrapper,\n", + " ImageOmeZarrWrapper,\n", + " ObsSegmentationsOmeZarrWrapper,\n", + " get_initial_coordination_scope_prefix,\n", + " hconcat,\n", + " vconcat,\n", + ")\n", + "from vitessce.data_utils import (\n", + " VAR_CHUNK_SIZE,\n", + " generate_h5ad_ref_spec,\n", + " multiplex_img_to_ome_tiff,\n", + " multiplex_img_to_ome_zarr,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "Kclp", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "In this blog post, we perform basic data transformation tasks to save individual elements of an integrated SpatialData object to separate files in AnnData, OME-TIFF, and OME-Zarr formats.\n", + "To perform these data transformations, we import the following dependencies.\n", + "In general, you will typically not need to import all of these dependencies, either because you are only working with data in one of these formats, or because the data you intend to visualize is already saved to a file or directory." + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Download example dataset" + ] + }, + { + "cell_type": "markdown", + "id": "Hstk", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "We download a mouse liver dataset which serves as a SpatialData [example dataset](https://github.com/scverse/spatialdata-notebooks/blob/main/notebooks/examples/transformations.ipynb).\n", + "\n", + "This dataset was generated by [Guilliams et al.](https://doi.org/10.1016/j.cell.2021.12.018) and processed using [SPArrOW](https://doi.org/10.1101/2024.07.04.601829) during the SpatialData [developer workshop](https://doi.org/10.37044/osf.io/8ck3e) in 2024." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "nWHF", + "metadata": {}, + "outputs": [], + "source": [ + "base_url = \"https://storage.googleapis.com/vitessce-demo-data/spatialdata-may-2025\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "iLit", + "metadata": {}, + "outputs": [], + "source": [ + "zip_filepath = f\"{base_url}/mouse_liver.spatialdata.zarr.zip\"\n", + "spatialdata_filepath = f\"{base_url}/mouse_liver.spatialdata.zarr\"\n", + "adata_zarr_filepath = f\"{base_url}/mouse_liver.anndata.zarr\"\n", + "adata_h5ad_filepath = f\"{base_url}/mouse_liver.h5ad\"\n", + "ref_spec_json_filepath = f\"{base_url}/mouse_liver.h5ad.ref.json\"\n", + "ome_tiff_filepath = f\"{base_url}/mouse_liver.ome.tif\"\n", + "offsets_json_filepath = f\"{base_url}/mouse_liver.ome.tif.offsets.json\"\n", + "ome_zarr_filepath = f\"{base_url}/mouse_liver.ome.zarr\"\n", + "labels_ome_zarr_filepath = f\"{base_url}/mouse_liver.labels.ome.zarr\"" + ] + }, + { + "cell_type": "markdown", + "id": "ZHCJ", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Visualization of a SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "ROlb", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "SpatialData objects are the most complex type of data structure we will work with in this blog post.\n", + "SpatialData objects function as contains for multiple types of Spatial Elements:\n", + "\n", + "- Tables (each table is represented as an AnnData object)\n", + "- Points (e.g., coordinates of transcripts from FISH-based experiments)\n", + "- Shapes (vector-based shapes such as polygons and circles)\n", + "- Labels (label images, i.e., segmentation bitmasks; each label image is stored using OME-Zarr)\n", + "- Images (microscopy images; each image is stored using OME-Zarr)" + ] + }, + { + "cell_type": "markdown", + "id": "qnkX", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views.\n", + "To visualize data stored in a SpatialData object, we use the `SpatialDataWrapper` class and specify the paths (relative to the root of the Zarr [directory store](https://zarr.readthedocs.io/en/v2.18.5/api/storage.html#zarr.storage.DirectoryStore)) to different spatial elements of interest." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "TqIu", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version='1.0.18', name='SpatialData Demo')\n", + "_wrapper = SpatialDataWrapper(sdata_url=spatialdata_filepath, table_path='tables/table', image_path='images/raw_image', obs_segmentations_path='labels/segmentation_mask', obs_feature_matrix_path='tables/table/X', obs_set_paths=['tables/table/obs/annotation'], obs_set_names=['Annotation'], region='nucleus_boundaries', coordinate_system='global', coordination_values={'obsType': 'cell'})\n", + "_dataset = vc.add_dataset(name='Mouse Liver').add_object(_wrapper)\n", + "_spatial = vc.add_view('spatialBeta', dataset=_dataset)\n", + "feature_list = vc.add_view('featureList', dataset=_dataset)\n", + "_layer_controller = vc.add_view('layerControllerBeta', dataset=_dataset)\n", + "obs_sets = vc.add_view('obsSets', dataset=_dataset)\n", + "_heatmap = vc.add_view('heatmap', dataset=_dataset)\n", + "[obs_color_encoding_scope] = vc.add_coordination('obsColorEncoding')\n", + "obs_color_encoding_scope.set_value('cellSetSelection')\n", + "vc.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image'))\n", + "vc.link_views_by_dict([_spatial, _layer_controller], {'segmentationLayer': CL([{'segmentationChannel': CL([{'obsColorEncoding': obs_color_encoding_scope}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'obsSegmentations'))\n", + "vc.link_views([_spatial, _layer_controller, feature_list, obs_sets, _heatmap], ['obsType'], [_wrapper.obs_type_label])\n", + "vc.link_views_by_dict([feature_list, obs_sets, _heatmap], {'obsColorEncoding': obs_color_encoding_scope}, meta=False)\n", + "vc.layout(_spatial / _heatmap | _layer_controller / (feature_list | obs_sets))" + ] + }, + { + "cell_type": "markdown", + "id": "Vxnm", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Render the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "DnEU", + "metadata": {}, + "outputs": [], + "source": [ + "_vw = vc.widget(height=900)\n", + "_vw" + ] + }, + { + "cell_type": "markdown", + "id": "ulZA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Extract AnnData object from SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "ecfG", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "The above example demonstrates how to visualize a spatial 'omics dataset containing not only single-cell information (e.g., a cell-by-gene expression matrix, cell type annotations) but also an image and cell segmentations.\n", + "To demonstrate how to use Vitessce to visualize data from a (non-spatial) single-cell experiment, we will extract this information from the SpatialData object and save it to a simpler [AnnData](https://anndata.readthedocs.io/) object (ignoring the imaging and spatially-resolved elements)." + ] + }, + { + "cell_type": "markdown", + "id": "Pvdt", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "As Zarr-formatted data can be easily visualized by Vitessce, we recommend saving the AnnData object to a Zarr store using the [write_zarr](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_zarr.html) method.\n", + "Optionally, the shape of array chunks (for the AnnData `X` array) can be specified as a parameter, to optimize performance based on data access patterns.\n", + "For example, a common pattern is to visualize data across all cells for one gene.\n", + "To support such a pattern, the chunk shape can be specified as follows, `(total number of cells, small number of genes)`, resulting in tall-and-skinny array chunks." + ] + }, + { + "cell_type": "markdown", + "id": "ZBYS", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Alternatively, your AnnData object may already be stored using the H5AD (HDF5-based) format.\n", + "To demonstrate this scenario, we save the object using the [write_h5ad](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_h5ad.html) method." + ] + }, + { + "cell_type": "markdown", + "id": "aLJB", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "To read H5AD-formatted data, Vitessce requires an accompanying JSON [references specification](https://fsspec.github.io/kerchunk/spec.html) file, which can be constructed using the `generate_h5ad_ref_spec` utility function." + ] + }, + { + "cell_type": "markdown", + "id": "nHfw", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Visualization of an AnnData object" + ] + }, + { + "cell_type": "markdown", + "id": "xXTn", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### Zarr-based AnnData" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "AjVT", + "metadata": {}, + "outputs": [], + "source": [ + "vc_1 = VitessceConfig(schema_version='1.0.18', name='AnnData (zarr)')\n", + "_wrapper = AnnDataWrapper(adata_url=adata_zarr_filepath, obs_feature_matrix_path='X', obs_set_paths=['obs/annotation'], obs_set_names=['Annotation'], coordination_values={'obsType': 'cell'})\n", + "_dataset = vc_1.add_dataset(name='Mouse Liver').add_object(_wrapper)\n", + "_heatmap = vc_1.add_view(vt.HEATMAP, dataset=_dataset)\n", + "feature_list_1 = vc_1.add_view(vt.FEATURE_LIST, dataset=_dataset)\n", + "obs_sets_1 = vc_1.add_view(vt.OBS_SETS, dataset=_dataset)\n", + "_violin_plots = vc_1.add_view('obsSetFeatureValueDistribution', dataset=_dataset)\n", + "vc_1.link_views([_heatmap, feature_list_1, obs_sets_1], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]])\n", + "vc_1.layout(_heatmap / _violin_plots | feature_list_1 / obs_sets_1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "pHFh", + "metadata": {}, + "outputs": [], + "source": [ + "vc_1.widget()" + ] + }, + { + "cell_type": "markdown", + "id": "NCOB", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### H5AD-based AnnData" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aqbW", + "metadata": {}, + "outputs": [], + "source": [ + "vc_2 = VitessceConfig(schema_version='1.0.18', name='AnnData (h5ad)')\n", + "_wrapper = AnnDataWrapper(adata_url=adata_h5ad_filepath, ref_url=ref_spec_json_filepath, obs_feature_matrix_path='X', obs_set_paths=['obs/annotation'], obs_set_names=['Annotation'], coordination_values={'obsType': 'cell'})\n", + "_dataset = vc_2.add_dataset(name='Mouse Liver').add_object(_wrapper)\n", + "_heatmap = vc_2.add_view(vt.HEATMAP, dataset=_dataset)\n", + "feature_list_2 = vc_2.add_view(vt.FEATURE_LIST, dataset=_dataset)\n", + "obs_sets_2 = vc_2.add_view(vt.OBS_SETS, dataset=_dataset)\n", + "_violin_plots = vc_2.add_view('obsSetFeatureValueDistribution', dataset=_dataset)\n", + "vc_2.link_views([_heatmap, feature_list_2, obs_sets_2], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]])\n", + "vc_2.layout(_heatmap / _violin_plots | feature_list_2 / obs_sets_2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "TRpd", + "metadata": {}, + "outputs": [], + "source": [ + "vc_2.widget()" + ] + }, + { + "cell_type": "markdown", + "id": "TXez", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Visualization of an image file" + ] + }, + { + "cell_type": "markdown", + "id": "dNNg", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### OME-Zarr image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "yCnT", + "metadata": {}, + "outputs": [], + "source": [ + "vc_3 = VitessceConfig(schema_version='1.0.18', name='Image (ome-zarr)')\n", + "img_wrapper = ImageOmeZarrWrapper(img_url=ome_zarr_filepath, coordination_values={'fileUid': 'image'})\n", + "segmentations_wrapper = ObsSegmentationsOmeZarrWrapper(img_url=labels_ome_zarr_filepath, coordination_values={'fileUid': 'segmentations'})\n", + "_dataset = vc_3.add_dataset(name='Mouse Liver').add_object(img_wrapper).add_object(segmentations_wrapper)\n", + "_spatial = vc_3.add_view('spatialBeta', dataset=_dataset)\n", + "_layer_controller = vc_3.add_view('layerControllerBeta', dataset=_dataset)\n", + "vc_3.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'fileUid': 'image', 'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image'))\n", + "vc_3.link_views_by_dict([_spatial, _layer_controller], {'segmentationLayer': CL([{'fileUid': 'segmentations', 'segmentationChannel': CL([{'obsColorEncoding': 'spatialChannelColor', 'spatialChannelColor': [0, 255, 0], 'spatialChannelOpacity': 0.75, 'spatialSegmentationFilled': False, 'spatialSegmentationStrokeWidth': 0.25}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'obsSegmentations'))\n", + "vc_3.link_views([_spatial, _layer_controller, feature_list_2, obs_sets_2], ['obsType'], ['cell'])\n", + "vc_3.layout(hconcat(_spatial, _layer_controller, split=(2, 1)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "wlCL", + "metadata": {}, + "outputs": [], + "source": [ + "_vw = vc_3.widget()\n", + "_vw" + ] + }, + { + "cell_type": "markdown", + "id": "kqZH", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### OME-TIFF image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "wAgl", + "metadata": {}, + "outputs": [], + "source": [ + "vc_4 = VitessceConfig(schema_version='1.0.18', name='Image and segmentations (ome-tiff)')\n", + "_wrapper = ImageOmeTiffWrapper(img_url=ome_tiff_filepath, offsets_url=offsets_json_filepath)\n", + "_dataset = vc_4.add_dataset(name='Mouse Liver').add_object(_wrapper)\n", + "_spatial = vc_4.add_view('spatialBeta', dataset=_dataset)\n", + "_layer_controller = vc_4.add_view('layerControllerBeta', dataset=_dataset)\n", + "vc_4.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image'))\n", + "vc_4.layout(hconcat(_spatial, _layer_controller, split=(2, 1)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "rEll", + "metadata": {}, + "outputs": [], + "source": [ + "vc_4.widget()" + ] + }, + { + "cell_type": "markdown", + "id": "dGlV", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Data location options\n", + "\n", + "Vitessce can visualize data [not only stored](https://python-docs.vitessce.io/data_options.html) locally (referenced using local file or directory paths) but also stored remotely (referenced using absolute URL paths).\n", + "Depending on whether the Python kernel running the Jupyter process is running locally versus remotely (e.g., on a cluster or cloud platform such as Google Colab), certain data locations may be challenging to access from the machine running the Jupyter notebook frontend (i.e., the machine on which the web browser used to view the notebook is installed).\n", + "\n", + "To provide data via one of these alternative mechanisms, use parameters with the following suffices when instantiating the data wrapper classes.\n", + "\n", + "- `_path`: Local file or directory\n", + "- `_url`: Remote file or directory\n", + "- `_store`: Zarr-store-accessible (for zarr-based formats)\n", + "- `_artifact`: Lamin artifact\n", + "\n", + "For example, `adata_path` can be exchanged for one of the following options.\n", + "\n", + "```diff\n", + "AnnDataWrapper(\n", + "- adata_path=\"./mouse_liver.spatialdata.zarr\",\n", + "+ adata_url=\"https://example.com/mouse_liver.spatialdata.zarr\", # Absolute URL\n", + "+ adata_store=\"./mouse_liver.spatialdata.zarr\", # String interpreted as root of DirectoryStore\n", + "+ adata_store=zarr.DirectoryStore(\"./mouse_liver.spatialdata.zarr\"), # Instance of zarr.storage\n", + "+ adata_artifact=adata_zarr_artifact, # Instance of ln.Artifact\n", + " ...\n", + "```\n", + "\n", + "Note that the `_store` options are only available for Zarr-based formats, such as AnnDataWrapper, SpatialDataWrapper, and ImageOmeZarrWrapper.\n", + "Further, when multiple files are required, such as both `adata_path` and `ref_path` (for the JSON reference specification file accompanying an H5AD file) or `img_path` and `offsets_path`, multiple parameter suffices may need to be changed." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/spatial_data_visium_hd.ipynb b/docs/notebooks/__ipynb__/spatial_data_visium_hd.ipynb new file mode 100644 index 00000000..dd1dd599 --- /dev/null +++ b/docs/notebooks/__ipynb__/spatial_data_visium_hd.ipynb @@ -0,0 +1,279 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Vitessce Widget Tutorial" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a SpatialData object" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "import zipfile\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " CoordinationLevel as CL,\n", + " SpatialDataWrapper,\n", + " get_initial_coordination_scope_prefix\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "data_dir = \"data\"\n", + "zip_filepath = join(data_dir, \"visium_hd_3.0.0_io.zip\")\n", + "spatialdata_filepath = join(data_dir, \"visium_hd_3.0.0.spatialdata.zarr\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "if not isdir(spatialdata_filepath):\n", + " if not isfile(zip_filepath):\n", + " os.makedirs(data_dir, exist_ok=True)\n", + " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/visium_hd_3.0.0_io.zip', zip_filepath)\n", + " with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n", + " zip_ref.extractall(data_dir)\n", + " os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Rasterize bins\n", + "Reference: https://spatialdata.scverse.org/en/stable/tutorials/notebooks/notebooks/examples/technology_visium_hd.html#performant-on-the-fly-data-rasterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "SFPL", + "metadata": {}, + "outputs": [], + "source": [ + "from spatialdata import (\n", + " read_zarr,\n", + " rasterize_bins,\n", + " rasterize_bins_link_table_to_labels\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "sdata = read_zarr(spatialdata_filepath)\n", + "\n", + "for bin_size in [\"016\", \"008\", \"002\"]:\n", + " # rasterize_bins() requires a compresed sparse column (csc) matrix\n", + " sdata.tables[f\"square_{bin_size}um\"].X = sdata.tables[f\"square_{bin_size}um\"].X.tocsc()\n", + " rasterized = rasterize_bins(\n", + " sdata,\n", + " f\"Visium_HD_Mouse_Small_Intestine_square_{bin_size}um\",\n", + " f\"square_{bin_size}um\",\n", + " \"array_col\",\n", + " \"array_row\",\n", + " # We want to rasterize to a Labels element, rather than an Image element.\n", + " return_region_as_labels=True\n", + " )\n", + " sdata[f\"rasterized_{bin_size}um\"] = rasterized\n", + " rasterize_bins_link_table_to_labels(\n", + " sdata,\n", + " table_name=f\"square_{bin_size}um\",\n", + " rasterized_labels_name=f\"rasterized_{bin_size}um\",\n", + " )\n", + " try:\n", + " sdata.write_element(f\"rasterized_{bin_size}um\")\n", + " except:\n", + " pass\n", + "\n", + "sdata" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "RGSE", + "metadata": {}, + "outputs": [], + "source": [ + "sdata" + ] + }, + { + "cell_type": "markdown", + "id": "Kclp", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## Configure Vitessce\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "emfo", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(\n", + " schema_version=\"1.0.18\",\n", + " name='Visium HD SpatialData Demo',\n", + ")\n", + "# Add data to the configuration:\n", + "wrapper = SpatialDataWrapper(\n", + " sdata_path=spatialdata_filepath,\n", + " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", + " image_path=\"images/Visium_HD_Mouse_Small_Intestine_full_image\",\n", + " table_path=\"tables/square_016um\",\n", + " obs_feature_matrix_path=\"tables/square_016um/X\",\n", + " obs_segmentations_path=\"labels/rasterized_016um\",\n", + " #region=\"CytAssist_FFPE_Human_Breast_Cancer\",\n", + " coordinate_system=\"Visium_HD_Mouse_Small_Intestine\",\n", + " coordination_values={\n", + " # The following tells Vitessce to consider each observation as a \"bin\"\n", + " \"obsType\": \"bin\",\n", + " }\n", + ")\n", + "dataset = vc.add_dataset(name='Visium HD').add_object(wrapper)\n", + "\n", + "# Add views (visualizations) to the configuration:\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "feature_list = vc.add_view(\"featureList\", dataset=dataset)\n", + "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'imageLayer': CL([{\n", + " 'photometricInterpretation': 'RGB',\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'segmentationLayer': CL([{\n", + " 'segmentationChannel': CL([{\n", + " 'spatialChannelOpacity': 0.5,\n", + " 'obsColorEncoding': 'geneSelection',\n", + " 'featureValueColormapRange': [0, 0.5],\n", + " }])\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", + "obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", + "vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType', 'featureSelection'], [wrapper.obs_type_label, ['AA986860']])\n", + "\n", + "# Layout the views\n", + "vc.layout(spatial | (feature_list / layer_controller / obs_sets));" + ] + }, + { + "cell_type": "markdown", + "id": "Hstk", + "metadata": {}, + "source": [ + "### Render the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "nWHF", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/web_app_brain.ipynb b/docs/notebooks/__ipynb__/web_app_brain.ipynb new file mode 100644 index 00000000..ba5aeb17 --- /dev/null +++ b/docs/notebooks/__ipynb__/web_app_brain.ipynb @@ -0,0 +1,344 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Vitessce Web App Tutorial" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of single-cell RNA seq data using vitessce.io" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Download the data\n", + "\n", + "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "os.makedirs(\"data\", exist_ok=True)\n", + "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", + "urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Load the data\n", + "\n", + "Note: this function may print a `FutureWarning`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "SFPL", + "metadata": {}, + "outputs": [], + "source": [ + "adata = read_h5ad(adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "BYtC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3.1. Preprocess the Data For Visualization\n", + "\n", + "This dataset contains 25,587 genes. In order to visualize it efficiently, we convert it to CSC sparse format so that we can make fast requests for gene data. We also prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "RGSE", + "metadata": {}, + "outputs": [], + "source": [ + "top_dispersion = adata.var[\"dispersions_norm\"][\n", + " sorted(\n", + " range(len(adata.var[\"dispersions_norm\"])),\n", + " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", + " )[-51:][0]\n", + "]\n", + "adata.var[\"top_highly_variable\"] = (\n", + " adata.var[\"dispersions_norm\"] > top_dispersion\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "Kclp", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Create the Vitessce widget configuration\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 4.1. Instantiate a `VitessceConfig` object\n", + "\n", + "Use the `VitessceConfig(name, description)` constructor to create an instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Hstk", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', description='COVID-19 Healthy Donor Brain')" + ] + }, + { + "cell_type": "markdown", + "id": "nWHF", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 4.2. Add a dataset to the `VitessceConfig` instance\n", + "\n", + "In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance.\n", + "\n", + "Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a \"data wrapper\" instance to our new dataset. For example, the `AnnDataWrapper` class knows how to convert AnnData objects to the corresponding Vitessce data types.\n", + "\n", + "Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `cell_set_obs_cols` to tell Vitessce which columns of the `obs` dataframe correspond to cell sets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "iLit", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", + " adata,\n", + " obs_embedding_paths=[\"obsm/X_umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/CellType\"],\n", + " obs_set_names=[\"Cell Type\"],\n", + " obs_feature_matrix_path=\"X\",\n", + " feature_filter_path=\"var/top_highly_variable\"\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ZHCJ", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 4.3. Add visualizations to the `VitessceConfig` instance\n", + "\n", + "Now that we have added a dataset, we can configure visualizations. The `.add_view(dataset, component_type)` method adds a view (i.e. visualization or controller component) to the configuration.\n", + "\n", + "The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter.\n", + "\n", + "For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ROlb", + "metadata": {}, + "outputs": [], + "source": [ + "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)" + ] + }, + { + "cell_type": "markdown", + "id": "qnkX", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 4.4. Define the visualization layout\n", + "\n", + "The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "TqIu", + "metadata": {}, + "outputs": [], + "source": [ + "vc.layout((scatterplot | cell_sets) / (heatmap | genes));" + ] + }, + { + "cell_type": "markdown", + "id": "Vxnm", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Launch the web application\n", + "\n", + "The `vc.web_app()` method serves the processed data locally and opens a web browser to `http://vitessce.io/?url={config_as_json}`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "DnEU", + "metadata": {}, + "outputs": [], + "source": [ + "vc.web_app()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_anndata_with_image.ipynb b/docs/notebooks/__ipynb__/widget_anndata_with_image.ipynb new file mode 100644 index 00000000..3d1e38ca --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_anndata_with_image.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of AnnData object containing an image in `uns`" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Note: This approach to storing images within AnnData objects is no longer recommended now that [SpatialData](https://spatialdata.scverse.org/en/stable/) has been introduced." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import scanpy as sc\n", + "import numpy as np\n", + "from vitessce.data_utils import rgb_img_to_ome_zarr, VAR_CHUNK_SIZE\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " AnnDataWrapper,\n", + " ImageOmeZarrWrapper,\n", + ")\n", + "from os.path import join" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "output_img = join(\"data\", \"V1_Human_Lymph_Node.ome.zarr\")\n", + "output_adata = join(\"data\", \"V1_Human_Lymph_Node.anndata.zarr\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "adata = sc.datasets.visium_sge(sample_id=\"V1_Human_Lymph_Node\", include_hires_tiff=True)\n", + "\n", + "# Write img_arr to OME-Zarr.\n", + "# Need to convert images from interleaved to non-interleaved (color axis should be first).\n", + "img_hires = adata.uns['spatial']['V1_Human_Lymph_Node']['images']['hires']\n", + "img_arr = np.transpose(img_hires, (2, 0, 1))\n", + "# Convert values from [0, 1] to [0, 255].\n", + "img_arr *= 255.0\n", + "\n", + "# First, save the image to an OME-Zarr image format\n", + "rgb_img_to_ome_zarr(img_arr, output_img, axes=\"cyx\", chunks=(1, 256, 256), img_name=\"Image\")\n", + "# Second, save the AnnData object to Zarr format\n", + "adata.write_zarr(output_adata, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.17\", name=\"AnnData with image\")\n", + "dataset = vc.add_dataset(\"My dataset\").add_object(\n", + " AnnDataWrapper(\n", + " adata_path=output_adata,\n", + "\n", + " )\n", + ").add_object(\n", + " ImageOmeZarrWrapper(\n", + " img_path=output_img,\n", + " )\n", + ")\n", + "\n", + "spatial_view = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "lc_view = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "\n", + "vc.layout(spatial_view | lc_view);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_brain.ipynb b/docs/notebooks/__ipynb__/widget_brain.ipynb new file mode 100644 index 00000000..9e7cedb4 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_brain.ipynb @@ -0,0 +1,365 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of single-cell RNA seq data" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")\n", + "from vitessce.data_utils import (\n", + " optimize_adata,\n", + " VAR_CHUNK_SIZE,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Download the data\n", + "\n", + "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", + "if not isfile(adata_filepath):\n", + " os.makedirs(\"data\", exist_ok=True)\n", + " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Load the data\n", + "\n", + "Note: this function may print a `FutureWarning`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "adata = read_h5ad(adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3.1. Preprocess the Data For Visualization\n", + "\n", + "This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "top_dispersion = adata.var[\"dispersions_norm\"][\n", + " sorted(\n", + " range(len(adata.var[\"dispersions_norm\"])),\n", + " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", + " )[-51:][0]\n", + "]\n", + "adata.var[\"top_highly_variable\"] = (\n", + " adata.var[\"dispersions_norm\"] > top_dispersion\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3.2 Save the Data to Zarr store\n", + "\n", + "We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_filepath = join('data', 'habib17.processed.zarr')\n", + "if not isdir(zarr_filepath):\n", + " adata_1 = optimize_adata(adata, obs_cols=['CellType'], obsm_keys=['X_umap'], optimize_X=True, var_cols=['top_highly_variable'])\n", + " adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Create the Vitessce widget configuration\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." + ] + }, + { + "cell_type": "markdown", + "id": "Hstk", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 4.1. Instantiate a `VitessceConfig` object\n", + "\n", + "Use the `VitessceConfig` constructor to create an instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "nWHF", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', description='COVID-19 Healthy Donor Brain')" + ] + }, + { + "cell_type": "markdown", + "id": "iLit", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 4.2. Add a dataset to the `VitessceConfig` instance\n", + "\n", + "In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance.\n", + "\n", + "Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a \"data wrapper\" instance to our new dataset. For example, the `AnnDataWrapper` helps to configure AnnData Zarr stores for use in the Vitessce configuration.\n", + "\n", + "Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `obs_set_paths` to tell Vitessce that certain columns of the `obs` dataframe correspond to cell type annotations or cell clusterings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ZHCJ", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", + " adata_path=zarr_filepath,\n", + " obs_embedding_paths=[\"obsm/X_umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/CellType\"],\n", + " obs_set_names=[\"Cell Type\"],\n", + " obs_feature_matrix_path=\"X\",\n", + " initial_feature_filter_path=\"var/top_highly_variable\"\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ROlb", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 4.3. Add visualizations to the `VitessceConfig` instance\n", + "\n", + "Now that we have added a dataset, we can configure visualizations. The `.add_view` method adds a view (i.e. visualization or controller component) to the configuration.\n", + "\n", + "The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter.\n", + "\n", + "For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "qnkX", + "metadata": {}, + "outputs": [], + "source": [ + "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)" + ] + }, + { + "cell_type": "markdown", + "id": "TqIu", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 4.4. Define the visualization layout\n", + "\n", + "The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Vxnm", + "metadata": {}, + "outputs": [], + "source": [ + "vc.layout((scatterplot | cell_sets) / (heatmap | genes));" + ] + }, + { + "cell_type": "markdown", + "id": "DnEU", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Create the widget\n", + "\n", + "The `vc.widget()` method returns the configured widget instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ulZA", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_brain_h5ad.ipynb b/docs/notebooks/__ipynb__/widget_brain_h5ad.ipynb new file mode 100644 index 00000000..67dc0d6a --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_brain_h5ad.ipynb @@ -0,0 +1,198 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of single-cell RNA seq data from H5AD file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "import json\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")\n", + "from vitessce.data_utils import (\n", + " generate_h5ad_ref_spec\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 0. Download data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "h5_url = \"https://datasets.cellxgene.cziscience.com/84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "adata_filepath = join(\"data\", \"84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad\")\n", + "if not isfile(adata_filepath):\n", + " os.makedirs(\"data\", exist_ok=True)\n", + " urlretrieve(h5_url, adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Create a Reference Spec JSON file for the H5AD file\n", + "\n", + "In order for Vitessce to load H5AD files, we also need to provide a corresponding [Reference Spec](https://fsspec.github.io/kerchunk/spec.html) JSON file which contains mappings between AnnData object keys and the byte offsets at which those AnnData object values begin within the H5AD file binary contents." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "json_filepath = join(\"data\", \"84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad.reference.json\")\n", + "if not isfile(json_filepath):\n", + " ref_dict = generate_h5ad_ref_spec(h5_url)\n", + " with open(json_filepath, \"w\") as f:\n", + " json.dump(ref_dict, f)" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Create the Vitessce widget configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.17\", name='Nakshatri et al', description='snRNA-seq analyses of breast tissues of healthy women of diverse genetic ancestry')\n", + "\n", + "dataset = vc.add_dataset(name='84df8fa1').add_object(AnnDataWrapper(\n", + " adata_path=adata_filepath,\n", + " ref_path=json_filepath, # We specify paths to both the H5AD and JSON files\n", + " obs_embedding_paths=[\"obsm/X_wnn.umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/cell_type\"],\n", + " obs_set_names=[\"Cell Type\"],\n", + " obs_feature_matrix_path=\"X\",\n", + " )\n", + ")\n", + "\n", + "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "cell_set_sizes = vc.add_view(cm.OBS_SET_SIZES, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "\n", + "vc.layout((scatterplot | cell_sets) / (cell_set_sizes | genes));" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Create the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_brain_with_base_dir.ipynb b/docs/notebooks/__ipynb__/widget_brain_with_base_dir.ipynb new file mode 100644 index 00000000..2eb26bb6 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_brain_with_base_dir.ipynb @@ -0,0 +1,422 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Configure relative to a base_dir" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + " BASE_URL_PLACEHOLDER,\n", + ")\n", + "from vitessce.data_utils import (\n", + " optimize_adata,\n", + " VAR_CHUNK_SIZE,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Define a `base_dir`\n", + "\n", + "We will define a `base_dir` inside which our data will live. We will provide this to `VitessceConfig` in order to construct a configuration that contains URL paths relative to this directory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "BASE_DIR = \"data\"" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Download the data\n", + "\n", + "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "adata_relative_filepath = \"habib17.processed.h5ad\" # Relative to BASE_DIR\n", + "adata_filepath = join(BASE_DIR, adata_relative_filepath)\n", + "if not isfile(adata_filepath):\n", + " os.makedirs(BASE_DIR, exist_ok=True)\n", + " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Load the data\n", + "\n", + "Note: this function may print a `FutureWarning`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "adata = read_h5ad(adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4.1. Preprocess the Data For Visualization\n", + "\n", + "This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "top_dispersion = adata.var[\"dispersions_norm\"][\n", + " sorted(\n", + " range(len(adata.var[\"dispersions_norm\"])),\n", + " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", + " )[-51:][0]\n", + "]\n", + "adata.var[\"top_highly_variable\"] = (\n", + " adata.var[\"dispersions_norm\"] > top_dispersion\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4.2 Save the Data to Zarr store\n", + "\n", + "We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Hstk", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_relative_filepath = 'habib17.processed.zarr'\n", + "zarr_filepath = join(BASE_DIR, zarr_relative_filepath)\n", + "if not isdir(zarr_filepath):\n", + " adata_1 = optimize_adata(adata, obs_cols=['CellType'], obsm_keys=['X_umap'], optimize_X=True, var_cols=['top_highly_variable'])\n", + " adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "markdown", + "id": "nWHF", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Create the Vitessce widget configuration\n", + "\n", + "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." + ] + }, + { + "cell_type": "markdown", + "id": "iLit", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 5.1. Instantiate a `VitessceConfig` object\n", + "\n", + "Use the `VitessceConfig` constructor to create an instance. In this case, we want to construct our configuration using local data that is relative to a particular directory, so we provide the `base_dir` parameter.\n", + "\n", + "Note: This `base_dir` parameter is optional. When it is omitted, local data paths are assumed to be relative to the current working directory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ZHCJ", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', base_dir=BASE_DIR)" + ] + }, + { + "cell_type": "markdown", + "id": "ROlb", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 5.2. Add a dataset to the `VitessceConfig` instance\n", + "\n", + "In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance.\n", + "\n", + "Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a \"data wrapper\" instance to our new dataset. For example, the `AnnDataWrapper` helps to configure AnnData Zarr stores for use in the Vitessce configuration.\n", + "\n", + "Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `obs_set_paths` to tell Vitessce that certain columns of the `obs` dataframe correspond to cell type annotations or cell clusterings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "qnkX", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", + " adata_path=zarr_relative_filepath, # Relative to BASE_DIR (because we specified base_dir in the VitessceConfig constructor)\n", + " obs_embedding_paths=[\"obsm/X_umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/CellType\"],\n", + " obs_set_names=[\"Cell Type\"],\n", + " obs_feature_matrix_path=\"X\",\n", + " initial_feature_filter_path=\"var/top_highly_variable\"\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "TqIu", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 5.3. Add visualizations to the `VitessceConfig` instance\n", + "\n", + "Now that we have added a dataset, we can configure visualizations. The `.add_view` method adds a view (i.e. visualization or controller component) to the configuration.\n", + "\n", + "The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter.\n", + "\n", + "For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Vxnm", + "metadata": {}, + "outputs": [], + "source": [ + "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)" + ] + }, + { + "cell_type": "markdown", + "id": "DnEU", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "### 5.4. Define the visualization layout\n", + "\n", + "The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ulZA", + "metadata": {}, + "outputs": [], + "source": [ + "vc.layout((scatterplot | cell_sets) / (heatmap | genes));" + ] + }, + { + "cell_type": "markdown", + "id": "ecfG", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 6. Create the widget\n", + "\n", + "The `vc.widget()` method returns the configured widget instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Pvdt", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + }, + { + "cell_type": "markdown", + "id": "ZBYS", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 7. Check the URLs in the configuration\n", + "\n", + "We can check that the data URLs in the configuration respected the specified `base_dir`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aLJB", + "metadata": {}, + "outputs": [], + "source": [ + "config_dict = vc.to_dict(base_url=BASE_URL_PLACEHOLDER)\n", + "config_dict" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_brain_with_quality_metric.ipynb b/docs/notebooks/__ipynb__/widget_brain_with_quality_metric.ipynb new file mode 100644 index 00000000..4bd0b706 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_brain_with_quality_metric.ipynb @@ -0,0 +1,289 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of single-cell RNA seq data" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")\n", + "from vitessce.data_utils import (\n", + " optimize_adata,\n", + " VAR_CHUNK_SIZE,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Download the data\n", + "\n", + "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", + "if not isfile(adata_filepath):\n", + " os.makedirs(\"data\", exist_ok=True)\n", + " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Load the data\n", + "\n", + "Note: this function may print a `FutureWarning`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "adata = read_h5ad(adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3.1. Preprocess the Data For Visualization\n", + "\n", + "This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "top_dispersion = adata.var[\"dispersions_norm\"][\n", + " sorted(\n", + " range(len(adata.var[\"dispersions_norm\"])),\n", + " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", + " )[-51:][0]\n", + "]\n", + "adata.var[\"top_highly_variable\"] = (\n", + " adata.var[\"dispersions_norm\"] > top_dispersion\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3.2 Save the Data to Zarr store\n", + "\n", + "We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_filepath = join(\"data\", \"habib17.h5ad.zarr\")\n", + "if not isdir(zarr_filepath):\n", + " adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "emfo", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(\n", + " schema_version=\"1.0.17\",\n", + " name='Habib et al',\n", + " description='COVID-19 Healthy Donor Brain'\n", + ")\n", + "\n", + "# Add data.\n", + "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", + " adata_path=zarr_filepath,\n", + " obs_embedding_paths=[\"obsm/X_umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/CellType\"],\n", + " obs_set_names=[\"Cell Type\"],\n", + " obs_feature_matrix_path=\"X\",\n", + " initial_feature_filter_path=\"var/top_highly_variable\",\n", + " coordination_values={\n", + " \"obsType\": 'cell',\n", + " \"featureType\": 'gene',\n", + " \"featureValueType\": 'expression',\n", + " },\n", + ")).add_object(AnnDataWrapper(\n", + " adata_path=zarr_filepath,\n", + " obs_feature_column_paths=[\"obs/percent_mito\"],\n", + " coordination_values={\n", + " \"obsType\": 'cell',\n", + " \"featureType\": 'qualityMetric',\n", + " \"featureValueType\": 'value',\n", + " }\n", + "))\n", + "\n", + "# Add views.\n", + "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "scatterplot_2 = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "histogram = vc.add_view(cm.FEATURE_VALUE_HISTOGRAM, dataset=dataset)\n", + "\n", + "# Link views.\n", + "\n", + "# Color one of the two scatterplots by the percent_mito quality metric.\n", + "# Also use this quality metric for the histogram values.\n", + "vc.link_views_by_dict([histogram, scatterplot_2], {\n", + " \"obsType\": 'cell',\n", + " \"featureType\": 'qualityMetric',\n", + " \"featureValueType\": 'value',\n", + " \"featureSelection\": [\"percent_mito\"],\n", + " \"obsColorEncoding\": \"geneSelection\",\n", + "}, meta=False)\n", + "\n", + "# Synchronize the zooming and panning of the two scatterplots\n", + "vc.link_views_by_dict([scatterplot, scatterplot_2], {\n", + " \"embeddingZoom\": None,\n", + " \"embeddingTargetX\": None,\n", + " \"embeddingTargetY\": None,\n", + "}, meta=False)\n", + "\n", + "# Define the layout.\n", + "vc.layout((scatterplot | (cell_sets / genes)) / (scatterplot_2 | histogram));" + ] + }, + { + "cell_type": "markdown", + "id": "Hstk", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Create the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "nWHF", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_from_dict.ipynb b/docs/notebooks/__ipynb__/widget_from_dict.ipynb new file mode 100644 index 00000000..35065dfe --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_from_dict.ipynb @@ -0,0 +1,123 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Using an existing view config dict" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the `VitessceConfig` class." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import VitessceConfig" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Import config of interest as a dict\n", + "\n", + "The Vitessce config at its lowest level is a JSON object or a Python `dict` that specifies a layout for the Vitessce components will be rendered in the widget. These components may be scatterplots, spatial plots, heatmaps, or control components. The config also describes the datasets and files that will be visualized.\n", + "\n", + "The `vitessce` package provides helper functions and classes to simplify the process of defining Vitessce configs. Those functions are demonstrated in the other notebooks. The helper functions are intended to make visualization of local datasets easy. However, in this case, we are importing a config that has been pre-defined in the file `example_configs.py`, in which the dataset being visualized is stored remotely in AWS S3 (rather than locally)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "from example_configs import dries as dries_config" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Create the Vitessce widget\n", + "\n", + "Create the widget by creating a new config instance using the `VitessceConfig.from_dict` static method, then calling the config's `.widget()` method.\n", + "\n", + "Render the widget by placing the widget variable on its own line at the end of the notebook cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig.from_dict(dries_config)\n", + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_genomic_profiles.ipynb b/docs/notebooks/__ipynb__/widget_genomic_profiles.ipynb new file mode 100644 index 00000000..5544627b --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_genomic_profiles.ipynb @@ -0,0 +1,253 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of genomic profiles" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " ViewType as vt,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + " MultivecZarrWrapper,\n", + ")\n", + "from vitessce.data_utils import (\n", + " adata_to_multivec_zarr,\n", + ")\n", + "from os.path import join\n", + "from scipy.io import mmread\n", + "import pandas as pd\n", + "import numpy as np\n", + "from anndata import AnnData" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Load the data\n", + "\n", + "In this step, we load the raw data that has been downloaded from the HuBMAP portal https://portal.hubmapconsortium.org/browse/dataset/210d118a14c8624b6bb9610a9062656e" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "mtx = mmread(join('data', 'snapatac', 'filtered_cell_by_bin.mtx')).toarray()\n", + "barcodes_df = pd.read_csv(join('data', 'snapatac', 'barcodes.txt'), header=None)\n", + "bins_df = pd.read_csv(join('data', 'snapatac', 'bins.txt'), header=None, names=[\"interval\"])\n", + "clusters_df = pd.read_csv(join('data', 'snapatac', 'umap_coords_clusters.csv'), index_col=0)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Convert the data to Vitessce-compatible formats\n", + "\n", + "Vitessce can load AnnData objects saved to Zarr formats efficiently." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "# The genome assembly is GRCh38 but the chromosome names in the bin names do not start with the \"chr\" prefix.\n", + "# This is incompatible with the chromosome names from `negspy`, so we need to append the prefix.\n", + "bins_df[\"interval\"] = bins_df[\"interval\"].apply(lambda x: \"chr\" + x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "SFPL", + "metadata": {}, + "outputs": [], + "source": [ + "obs = clusters_df[[\"cluster\"]]\n", + "obs[\"cluster\"] = obs[\"cluster\"].astype(str)\n", + "obsm = { \"X_umap\": clusters_df[[\"umap.1\", \"umap.2\"]].values }\n", + "adata = AnnData(X=mtx, obs=obs, var=bins_df, obsm=obsm)\n", + "adata" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "multivec_zarr_path = join(\"data\", \"HBM485.TBWH.322.multivec.zarr\")\n", + "adata_zarr_path = join(\"data\", \"HBM485.TBWH.322.adata.zarr\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "RGSE", + "metadata": {}, + "outputs": [], + "source": [ + "# Sort cluster IDs\n", + "cluster_ids = obs[\"cluster\"].unique().tolist()\n", + "cluster_ids.sort(key=int)\n", + "# Save genomic profiles to multivec-zarr format.\n", + "adata_to_multivec_zarr(adata, multivec_zarr_path, obs_set_col=\"cluster\", obs_set_name=\"Cluster\", obs_set_vals=cluster_ids)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "# Save anndata object to AnnData-Zarr format.\n", + "adata.write_zarr(adata_zarr_path)" + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Make a Vitessce configuration\n", + "\n", + "We need to tell Vitessce about the data that we want to load and the visualization components that we want to include in the widget.\n", + "For this dataset, we want to add the `GENOMIC_PROFILES` component, which renders genome browser tracks with [HiGlass](http://higlass.io)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Hstk", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='HuBMAP snATAC-seq')\n", + "dataset = vc.add_dataset(name='HBM485.TBWH.322').add_object(MultivecZarrWrapper(\n", + " zarr_path=multivec_zarr_path\n", + ")).add_object(AnnDataWrapper(\n", + " adata_path=adata_zarr_path,\n", + " obs_embedding_paths=[\"obsm/X_umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/cluster\"],\n", + " obs_set_names=[\"Cluster\"],\n", + "))\n", + "\n", + "genomic_profiles = vc.add_view(vt.GENOMIC_PROFILES, dataset=dataset)\n", + "scatter = vc.add_view(vt.SCATTERPLOT, dataset=dataset, mapping = \"UMAP\")\n", + "cell_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", + "\n", + "vc.layout(genomic_profiles / (scatter | cell_sets));" + ] + }, + { + "cell_type": "markdown", + "id": "nWHF", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Create the widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "iLit", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget(height=800)\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_imaging.ipynb b/docs/notebooks/__ipynb__/widget_imaging.ipynb new file mode 100644 index 00000000..cde3f76a --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_imaging.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of Multi-Modal Imaging Data\n", + "We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " OmeTiffWrapper,\n", + " MultiImageWrapper,\n", + ")\n", + "from os.path import join" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Configure Vitessce\n", + "Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", + "dataset = vc.add_dataset(name='Spraggins').add_object(\n", + " MultiImageWrapper(\n", + " image_wrappers=[\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode')\n", + " ],\n", + " use_physical_size_scaling=True,\n", + " )\n", + ")\n", + "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", + "status = vc.add_view(cm.STATUS, dataset=dataset)\n", + "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True)\n", + "vc.layout(spatial | (lc / status));" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Create the Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_imaging_beta.ipynb b/docs/notebooks/__ipynb__/widget_imaging_beta.ipynb new file mode 100644 index 00000000..da1a6413 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_imaging_beta.ipynb @@ -0,0 +1,160 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of Multi-Modal Imaging Data\n", + "We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " OmeTiffWrapper,\n", + " MultiImageWrapper,\n", + " CoordinationLevel as CL,\n", + ")\n", + "from os.path import join" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Configure Vitessce\n", + "Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.16\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", + "dataset = vc.add_dataset(name='Spraggins').add_file(\n", + " url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=',\n", + " file_type=\"image.ome-tiff\",\n", + " coordination_values={\n", + " \"fileUid\": \"PAS\",\n", + " },\n", + ")\n", + "\n", + "imageScopes = vc.add_coordination_by_dict({\n", + " \"imageLayer\": CL([\n", + " {\n", + " \"fileUid\": 'PAS',\n", + " \"spatialLayerOpacity\": 1,\n", + " \"spatialLayerVisible\": True,\n", + " \"photometricInterpretation\": 'RGB',\n", + " \"imageChannel\": CL([\n", + " {\n", + " \"spatialTargetC\": 0,\n", + " \"spatialChannelColor\": [255, 0, 0],\n", + " \"spatialChannelVisible\": True,\n", + " \"spatialChannelOpacity\": 1.0,\n", + " \"spatialChannelWindow\": [0, 255],\n", + " },\n", + " {\n", + " \"spatialTargetC\": 1,\n", + " \"spatialChannelColor\": [0, 255, 0],\n", + " \"spatialChannelVisible\": True,\n", + " \"spatialChannelOpacity\": 1.0,\n", + " \"spatialChannelWindow\": [0, 255],\n", + " },\n", + " {\n", + " \"spatialTargetC\": 2,\n", + " \"spatialChannelColor\": [0, 0, 255],\n", + " \"spatialChannelVisible\": True,\n", + " \"spatialChannelOpacity\": 1.0,\n", + " \"spatialChannelWindow\": [0, 255],\n", + " },\n", + " ]),\n", + " }\n", + " ])\n", + "})\n", + "\n", + "metaCoordinationScope = vc.add_meta_coordination()\n", + "metaCoordinationScope.use_coordination_by_dict(imageScopes)\n", + "\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "lc = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "\n", + "spatial.use_meta_coordination(metaCoordinationScope)\n", + "lc.use_meta_coordination(metaCoordinationScope)\n", + "\n", + "vc.layout(spatial | lc);" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Create the Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget(custom_js_url='http://localhost:8000/packages/main/prod/dist/index.min.js')\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_imaging_segmentation.ipynb b/docs/notebooks/__ipynb__/widget_imaging_segmentation.ipynb new file mode 100644 index 00000000..c46df0e8 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_imaging_segmentation.ipynb @@ -0,0 +1,119 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of Segmentation Bitmask\n", + "We visualize raw imaging data + a segmentation bitmask the [MCMicro piplene](https://mcmicro.org/) - see https://www.biorxiv.org/content/10.1101/2021.03.15.435473v1.full and specifically [Figure S1](https://www.google.com/url?q=https://www.biorxiv.org/content/10.1101/2021.03.15.435473v1.full%23F3&sa=D&source=editors&ust=1623173627976000&usg=AOvVaw3JkzCxYyE86q8jxfNCgShh)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " OmeTiffWrapper,\n", + " MultiImageWrapper,\n", + ")\n", + "from os.path import join" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Configure Vitessce\n", + "Set up the two images, already pyramidal from the [bioformats2raw + raw2ometiff pipeline](https://github.com/hms-dbmi/viv/tree/master/tutorial), labeling the segmentation \"on top\" as the bitmask and the other as simply the image data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='MCMicro Bitmask Visualization', description='Segmentation + Data of Exemplar 001')\n", + "dataset = vc.add_dataset(name='MCMicro').add_object(\n", + " MultiImageWrapper(\n", + " image_wrappers=[\n", + " OmeTiffWrapper(img_url='https://vitessce-demo-data.storage.googleapis.com/exemplar-001/exemplar-001.pyramid.ome.tif', name='Image'),\n", + " OmeTiffWrapper(img_url='https://vitessce-demo-data.storage.googleapis.com/exemplar-001/cellMask.pyramid.ome.tif', name='Mask', is_bitmask=True),\n", + " ]\n", + " )\n", + ")\n", + "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", + "status = vc.add_view(cm.STATUS, dataset=dataset)\n", + "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset)\n", + "vc.layout(spatial | (lc / status));" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Create the Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_loom.ipynb b/docs/notebooks/__ipynb__/widget_loom.ipynb new file mode 100644 index 00000000..19344fdf --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_loom.ipynb @@ -0,0 +1,268 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of a Loom file" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_loom\n", + "import numpy as np\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")\n", + "from vitessce.data_utils import (\n", + " optimize_adata,\n", + " to_diamond,\n", + " VAR_CHUNK_SIZE,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Download data\n", + "\n", + "Download `osmFISH_SScortex_mouse_all_cells.loom` from http://loom.linnarssonlab.org/." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "loom_filepath = join(\"data\", \"osmFISH_SScortex_mouse_all_cells.loom\")\n", + "if not isfile(loom_filepath):\n", + " os.makedirs(\"data\", exist_ok=True)\n", + " urlretrieve('http://loom.linnarssonlab.org/clone/osmFISH/osmFISH_SScortex_mouse_all_cells.loom', loom_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Open Loom file with AnnData's read_loom" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "adata = read_loom(loom_filepath, obsm_names={\"tSNE\": [\"_tSNE_1\", \"_tSNE_2\"], \"spatial\": [\"X\", \"Y\"]})" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Generate pseudo-segmentations as diamond-shaped polygons centered on the spatial coordinate of each cell, and store in `adata.obsm[\"segmentations\"]`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "num_cells = adata.obs.shape[0]\n", + "adata.obsm[\"segmentations\"] = np.zeros((num_cells, 4, 2))\n", + "radius = 100\n", + "for i in range(num_cells):\n", + " adata.obsm[\"segmentations\"][i, :, :] = to_diamond(adata.obsm['spatial'][i, 0], adata.obsm['spatial'][i, 1], radius)" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "Save the AnnData object to a Zarr store:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_filepath = join('data', 'osmFISH_SScortex_mouse_all_cells.zarr')\n", + "if not isdir(zarr_filepath) or True:\n", + " adata_1 = optimize_adata(adata, obs_cols=['ClusterName'], obsm_keys=['tSNE', 'spatial', 'segmentations'], optimize_X=True)\n", + " adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Configure Vitessce\n", + "\n", + "Create a Vitessce view config." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Hstk", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Loom Example', description='osmFISH dataset of the mouse cortex including all cells')\n", + "w = AnnDataWrapper(adata_path=zarr_filepath, obs_set_paths=[\"obs/ClusterName\"], obs_set_names=[\"Clusters\"], obs_locations_path=\"obsm/spatial\", obs_segmentations_path=\"obsm/segmentations\", obs_embedding_paths=[\"obsm/tSNE\"])\n", + "dataset = vc.add_dataset(name='SScortex').add_object(w)\n", + "\n", + "tsne = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"tSNE\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", + "\n", + "spatial_segmentation_layer_value = {\n", + " \"opacity\": 1,\n", + " \"radius\": 0,\n", + " \"visible\": True,\n", + " \"stroked\": False\n", + "}\n", + "\n", + "vc.link_views([spatial], [ct.SPATIAL_ZOOM, ct.SPATIAL_TARGET_X, ct.SPATIAL_TARGET_Y, ct.SPATIAL_SEGMENTATION_LAYER], [-6.43, 10417.69, 24885.55, spatial_segmentation_layer_value])\n", + "vc.layout(spatial | (tsne / cell_sets));" + ] + }, + { + "cell_type": "markdown", + "id": "nWHF", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Render the widget" + ] + }, + { + "cell_type": "markdown", + "id": "iLit", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "A widget can be created with the `.widget()` method on the config instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ZHCJ", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_modify_config.ipynb b/docs/notebooks/__ipynb__/widget_modify_config.ipynb new file mode 100644 index 00000000..b3b6af1e --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_modify_config.ipynb @@ -0,0 +1,153 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Programmatic modification of widget configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " OmeTiffWrapper,\n", + " MultiImageWrapper,\n", + " CoordinationLevel as CL,\n", + " ObsSegmentationsOmeTiffWrapper,\n", + " ImageOmeTiffWrapper,\n", + " get_initial_coordination_scope_prefix,\n", + ")\n", + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.16\")\n", + "dataset = vc.add_dataset(name='Spraggins').add_object(\n", + " ImageOmeTiffWrapper(\n", + " img_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.ome.tif\",\n", + " offsets_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.offsets.json\"\n", + " )\n", + ").add_object(\n", + " ObsSegmentationsOmeTiffWrapper(\n", + " img_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.ome.tif\",\n", + " offsets_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.offsets.json\",\n", + " obs_types_from_channel_names=True\n", + " )\n", + ")\n", + "\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "lc = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "\n", + "vc.link_views_by_dict([spatial, lc], {\n", + " \"imageLayer\": CL([\n", + " {\n", + " \"photometricInterpretation\": \"RGB\"\n", + " }\n", + " ]),\n", + "}, meta=True, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", + "\n", + "vc.layout(spatial | lc);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget(remount_on_uid_change=False)\n", + "vw" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "# Inspect the current configuration value.\n", + "# This is a dict in the JSON-based format https://vitessce.io/docs/view-config-json/\n", + "vw.config" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "# Programatically set a different zoom level and toggle the visibility/color of different segmentation layers:\n", + "vw.config = {\n", + " **vw.config,\n", + " # Need to provide a fresh \"uid\" value.\n", + " # This will tell Vitessce that the contents should be diff-ed against the previous config.\n", + " \"uid\": f\"new_config_{random.random()}\",\n", + " \"coordinationSpace\": {\n", + " # Information about the coordination space can be found at https://vitessce.io/docs/coordination-types/\n", + " **vw.config[\"coordinationSpace\"],\n", + " \"spatialZoom\": {\n", + " **vw.config[\"coordinationSpace\"][\"spatialZoom\"],\n", + " \"A\": -8\n", + " },\n", + " \"spatialChannelVisible\": {\n", + " **vw.config[\"coordinationSpace\"][\"spatialChannelVisible\"],\n", + " \"init_A_obsSegmentations_0\": True,\n", + " \"init_A_obsSegmentations_1\": False,\n", + " \"init_A_obsSegmentations_2\": False,\n", + " \"init_A_obsSegmentations_3\": False\n", + " },\n", + " \"spatialChannelColor\": {\n", + " **vw.config[\"coordinationSpace\"][\"spatialChannelColor\"],\n", + " \"init_A_obsSegmentations_0\": [255, 0, 0],\n", + " }\n", + " }\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_neuroglancer.ipynb b/docs/notebooks/__ipynb__/widget_neuroglancer.ipynb new file mode 100644 index 00000000..6bdaef0b --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_neuroglancer.ipynb @@ -0,0 +1,233 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Example usage of Neuroglancer view" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " ImageOmeTiffWrapper,\n", + " CsvWrapper,\n", + " hconcat,\n", + " vconcat,\n", + " get_initial_coordination_scope_prefix,\n", + " CoordinationLevel as CL\n", + ")\n", + "from os.path import join" + ] + }, + { + "cell_type": "markdown", + "id": "vblA", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Configure Vitessce" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.17\")\n", + "dataset = vc.add_dataset(name='Meshes').add_object(\n", + " ImageOmeTiffWrapper(\n", + " img_url='https://lsp-public-data.s3.amazonaws.com/yapp-2023-3d-melanoma/Dataset1-LSP13626-invasive-margin.ome.tiff',\n", + " offsets_url='https://lsp-public-data.s3.amazonaws.com/yapp-2023-3d-melanoma/Dataset1-LSP13626-invasive-margin.offsets.json',\n", + " coordination_values={\n", + " \"fileUid\": 'melanoma',\n", + " },\n", + " )\n", + ").add_object(\n", + " CsvWrapper(\n", + " data_type=\"obsEmbedding\",\n", + " csv_url='https://storage.googleapis.com/vitessce-demo-data/neuroglancer-march-2025/melanoma_with_embedding_filtered_ids.csv',\n", + " options= {\n", + " \"obsIndex\": 'id',\n", + " \"obsEmbedding\": ['tSNE1', 'tSNE2'],\n", + " },\n", + " coordination_values= {\n", + " \"obsType\": 'cell',\n", + " \"embeddingType\": 'TSNE',\n", + " },\n", + " )\n", + ").add_object(\n", + " CsvWrapper(\n", + " data_type=\"obsSets\",\n", + " csv_url='https://storage.googleapis.com/vitessce-demo-data/neuroglancer-march-2025/melanoma_with_embedding_filtered_ids.csv',\n", + " coordination_values={\n", + " \"obsType\": 'cell',\n", + " },\n", + " options= {\n", + " \"obsIndex\": 'id',\n", + " \"obsSets\": [\n", + " {\n", + " \"name\": 'Clusters',\n", + " \"column\": 'cluster',\n", + " },\n", + " ],\n", + " },\n", + " )\n", + ")\n", + "spatialThreeView = vc.add_view('spatialBeta', dataset=dataset);\n", + "lcView = vc.add_view('layerControllerBeta', dataset=dataset);\n", + "obsSets = vc.add_view('obsSets', dataset=dataset);\n", + "scatterView = vc.add_view('scatterplot', dataset=dataset, mapping=\"TSNE\");\n", + "# Configuration via props.viewerState is temporary and subject to change.\n", + "neuroglancerView = vc.add_view('neuroglancer', dataset=dataset).set_props(viewerState={\n", + " \"dimensions\": {\n", + " \"x\": [\n", + " 1e-9,\n", + " \"m\"\n", + " ],\n", + " \"y\": [\n", + " 1e-9,\n", + " \"m\"\n", + " ],\n", + " \"z\": [\n", + " 1e-9,\n", + " \"m\"\n", + " ]\n", + " },\n", + " \"position\": [\n", + " 49.5,\n", + " 1000.5,\n", + " 5209.5\n", + " ],\n", + " \"crossSectionScale\": 1,\n", + " \"projectionOrientation\": [\n", + " -0.636204183101654,\n", + " -0.5028395652770996,\n", + " 0.5443811416625977,\n", + " 0.2145828753709793\n", + " ],\n", + " \"projectionScale\": 1024,\n", + " \"layers\": [\n", + " {\n", + " \"type\": \"segmentation\",\n", + " \"source\": \"precomputed://https://vitessce-data-v2.s3.us-east-1.amazonaws.com/data/sorger/invasive_meshes\",\n", + " \"segments\": [\n", + " \"5\"\n", + " ],\n", + " \"segmentColors\": {\n", + " \"5\": \"red\"\n", + " },\n", + " \"name\": \"segmentation\"\n", + " }\n", + " ],\n", + " \"showSlices\": False,\n", + " \"layout\": \"3d\"\n", + "});\n", + "\n", + "vc.link_views([scatterView], ['embeddingObsRadiusMode', 'embeddingObsRadius'], ['manual', 4]);\n", + "\n", + "# Sync the zoom/rotation/pan states\n", + "vc.link_views_by_dict([spatialThreeView, lcView, neuroglancerView], {\n", + " \"spatialRenderingMode\": '3D',\n", + " \"spatialZoom\": 0,\n", + " \"spatialTargetT\": 0,\n", + " \"spatialTargetX\": 0,\n", + " \"spatialTargetY\": 0,\n", + " \"spatialTargetZ\": 0,\n", + " \"spatialRotationX\": 0,\n", + " \"spatialRotationY\": 0,\n", + "}, meta=False);\n", + "\n", + "# Initialize the image properties\n", + "vc.link_views_by_dict([spatialThreeView, lcView], {\n", + " \"imageLayer\": CL([\n", + " {\n", + " \"fileUid\": 'melanoma',\n", + " \"spatialLayerOpacity\": 1,\n", + " \"spatialTargetResolution\": None,\n", + " \"imageChannel\": CL([\n", + " {\n", + " \"spatialTargetC\": 0,\n", + " \"spatialChannelColor\": [255, 0, 0],\n", + " \"spatialChannelVisible\": True,\n", + " \"spatialChannelOpacity\": 1.0,\n", + " },\n", + " ]),\n", + " },\n", + " ]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image'));\n", + "\n", + "\n", + "vc.layout(hconcat(neuroglancerView, spatialThreeView, vconcat(lcView, obsSets, scatterView)));" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Create the Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_pbmc.ipynb b/docs/notebooks/__ipynb__/widget_pbmc.ipynb new file mode 100644 index 00000000..0fb8a0f7 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_pbmc.ipynb @@ -0,0 +1,231 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of 3k PBMC reference" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")\n", + "from vitessce.data_utils import (\n", + " optimize_adata,\n", + " VAR_CHUNK_SIZE,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Download the dataset\n", + "\n", + "Download `pbmc3k_final.h5ad` from https://seurat.nygenome.org/pbmc3k_final.h5ad" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "adata_filepath = join(\"data\", \"pbmc3k_final.h5ad\")\n", + "if not isfile(adata_filepath):\n", + " os.makedirs(\"data\", exist_ok=True)\n", + " urlretrieve('https://seurat.nygenome.org/pbmc3k_final.h5ad', adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Load the dataset\n", + "\n", + "Load the dataset using AnnData's `read_h5ad` function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "adata = read_h5ad(adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3.1 Save the AnnData object to Zarr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "zarr_filepath = join('data', 'pbmc3k_final.zarr')\n", + "if not isdir(zarr_filepath):\n", + " adata_1 = optimize_adata(adata, obs_cols=['leiden'], obsm_keys=['X_umap', 'X_pca'], optimize_X=True)\n", + " adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE])" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Create a Vitessce view config\n", + "\n", + "Define the data and views you would like to include in the widget.\n", + "\n", + "For more details about how to configure data depending on where the files are located relative to the notebook execution, see https://python-docs.vitessce.io/data_options.html." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='PBMC Reference')\n", + "dataset = vc.add_dataset(name='PBMC 3k').add_object(AnnDataWrapper(\n", + " adata_store=zarr_filepath,\n", + " obs_set_paths=[\"obs/leiden\"],\n", + " obs_set_names=[\"Leiden\"],\n", + " obs_embedding_paths=[\"obsm/X_umap\", \"obsm/X_pca\"],\n", + " obs_embedding_names=[\"UMAP\", \"PCA\"],\n", + " obs_feature_matrix_path=\"X\"\n", + "))\n", + "\n", + "umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"PCA\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)\n", + "\n", + "vc.layout((umap / pca) | ((cell_sets | genes) / heatmap));" + ] + }, + { + "cell_type": "markdown", + "id": "emfo", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 5. Create the Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Hstk", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_pbmc_remote.ipynb b/docs/notebooks/__ipynb__/widget_pbmc_remote.ipynb new file mode 100644 index 00000000..7b9ff492 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_pbmc_remote.ipynb @@ -0,0 +1,176 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of 3k PBMC reference from Remote Zarr Store" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "We need to import the classes and functions that we will be using from the corresponding packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Set the URL for the Remote Dataset\n", + "\n", + "For this example, we already have uploaded the `pbmc3k` dataset as a zarr store from the [scanpy docs](https://scanpy.readthedocs.io/en/stable/api/scanpy.datasets.pbmc3k.html) to the cloud." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "url = 'https://storage.googleapis.com/vitessce-demo-data/anndata-test/pbmc3k_processed.zarr/'" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Create a Vitessce view config\n", + "\n", + "Define the data and views you would like to include in the widget." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='PBMC Reference')\n", + "dataset = vc.add_dataset(name='PBMC 3k').add_object(AnnDataWrapper(adata_url=url, obs_set_paths=[\"obs/louvain\"], obs_set_names=[\"Louvain\"], obs_embedding_paths=[\"obsm/X_umap\", \"obsm/X_pca\"], obs_embedding_names=[\"UMAP\", \"PCA\"], obs_feature_matrix_path=\"X\"))\n", + "\n", + "umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", + "pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"PCA\")\n", + "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", + "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", + "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)\n", + "\n", + "vc.layout((umap / pca) | ((cell_sets | genes) / heatmap));" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 4. Create the Vitessce widget" + ] + }, + { + "cell_type": "markdown", + "id": "BYtC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "A widget can be created with the `.widget()` method on the config instance. Here, the `proxy=True` parameter allows this widget to be used in a cloud notebook environment, such as Binder." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "RGSE", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_plugin_custom.ipynb b/docs/notebooks/__ipynb__/widget_plugin_custom.ipynb new file mode 100644 index 00000000..ea9cad9a --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_plugin_custom.ipynb @@ -0,0 +1,193 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Vitessce custom plugin definition" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " OmeTiffWrapper,\n", + " MultiImageWrapper,\n", + " VitesscePlugin\n", + ")\n", + "from oxc_py import transform" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "PLUGIN_ESM = transform(\"\"\"\n", + "function createPlugins(utilsForPlugins) {\n", + " const {\n", + " React,\n", + " PluginFileType,\n", + " PluginViewType,\n", + " PluginCoordinationType,\n", + " PluginJointFileType,\n", + " z,\n", + " useCoordination,\n", + " invokeCommand,\n", + " } = utilsForPlugins;\n", + "\n", + " const CSS = `\n", + " .chat {\n", + " overflow-y: scroll;\n", + " }\n", + " `;\n", + "\n", + " function ChatView(props) {\n", + "\n", + " const [nextMessage, setNextMessage] = React.useState('');\n", + " const [isLoading, setIsLoading] = React.useState(false);\n", + " const [chatHistory, setChatHistory] = React.useState([]); // chatHistory is an array of message objects like [{ user, text }, ...]\n", + "\n", + " async function handleClick() { \n", + " setChatHistory(prev => ([\n", + " ...prev,\n", + " { user: 'You', text: nextMessage },\n", + " ]));\n", + " setIsLoading(true);\n", + " const [chatReceiveValue, chatReceiveBuffers] = await invokeCommand(\"chat_send\", nextMessage, []);\n", + " setChatHistory(prev => ([\n", + " ...prev,\n", + " { user: 'AI', text: chatReceiveValue.text },\n", + " ]));\n", + " setIsLoading(false);\n", + " }\n", + "\n", + " return (\n", + " <>\n", + " \n", + "
\n", + "

Chat view

\n", + "
\n", + " {chatHistory.map(message => (\n", + "

\n", + " {message.user}:\n", + " {message.text}\n", + "

\n", + " ))}\n", + "
\n", + " setNextMessage(e.target.value)} disabled={isLoading} />\n", + " \n", + "
\n", + " \n", + " );\n", + " }\n", + "\n", + " const pluginViewTypes = [\n", + " new PluginViewType('chat', ChatView, []),\n", + " ];\n", + " return { pluginViewTypes };\n", + "}\n", + "export default { createPlugins };\n", + "\"\"\")\n", + "\n", + "\n", + "def handle_chat_message(message, buffers):\n", + " return { \"text\": message.upper() }, []\n", + "\n", + "\n", + "class ChatPlugin(VitesscePlugin):\n", + " plugin_esm = PLUGIN_ESM\n", + " commands = {\n", + " \"chat_send\": handle_chat_message,\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", + "dataset = vc.add_dataset(name='Spraggins').add_object(\n", + " MultiImageWrapper(\n", + " image_wrappers=[\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode')\n", + " ],\n", + " use_physical_size_scaling=True,\n", + " )\n", + ")\n", + "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", + "status = vc.add_view(\"chat\", dataset=dataset)\n", + "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True)\n", + "vc.layout(spatial | (lc / status));" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Create the Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget(plugins=[ChatPlugin()])\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_plugin_demo.ipynb b/docs/notebooks/__ipynb__/widget_plugin_demo.ipynb new file mode 100644 index 00000000..13d3c4b4 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_plugin_demo.ipynb @@ -0,0 +1,145 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Vitessce plugin usage demo" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " OmeTiffWrapper,\n", + " MultiImageWrapper,\n", + ")\n", + "from os.path import join" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce.widget_plugins import DemoPlugin" + ] + }, + { + "cell_type": "markdown", + "id": "lEQa", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Configure Vitessce\n", + "Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "PKri", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.15\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", + "dataset = vc.add_dataset(name='Spraggins').add_object(\n", + " MultiImageWrapper(\n", + " image_wrappers=[\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'),\n", + " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode')\n", + " ],\n", + " use_physical_size_scaling=True,\n", + " )\n", + ")\n", + "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", + "status = vc.add_view(\"demo\", dataset=dataset)\n", + "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True)\n", + "vc.layout(spatial | (lc / status));" + ] + }, + { + "cell_type": "markdown", + "id": "Xref", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Create the Vitessce widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "SFPL", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget(plugins=[DemoPlugin()])\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_segmentations_beta.ipynb b/docs/notebooks/__ipynb__/widget_segmentations_beta.ipynb new file mode 100644 index 00000000..aef575d7 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_segmentations_beta.ipynb @@ -0,0 +1,105 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# Visualization of OME-TIFF images and segmentation masks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "MJUe", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " OmeTiffWrapper,\n", + " MultiImageWrapper,\n", + " CoordinationLevel as CL,\n", + " ObsSegmentationsOmeTiffWrapper,\n", + " ImageOmeTiffWrapper,\n", + " get_initial_coordination_scope_prefix,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "vc = VitessceConfig(schema_version=\"1.0.16\")\n", + "dataset = vc.add_dataset(name='Spraggins').add_object(\n", + " ImageOmeTiffWrapper(\n", + " img_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.ome.tif\",\n", + " offsets_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.offsets.json\"\n", + " )\n", + ").add_object(\n", + " ObsSegmentationsOmeTiffWrapper(\n", + " img_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.ome.tif\",\n", + " offsets_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.offsets.json\",\n", + " obs_types_from_channel_names=True\n", + " )\n", + ")\n", + "\n", + "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", + "lc = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", + "\n", + "vc.link_views_by_dict([spatial, lc], {\n", + " \"imageLayer\": CL([\n", + " {\n", + " \"photometricInterpretation\": \"RGB\" \n", + " }\n", + " ]),\n", + "}, meta=True, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", + "\n", + "vc.layout(spatial | lc);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bkHC", + "metadata": {}, + "outputs": [], + "source": [ + "vw = vc.widget()\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/__ipynb__/widget_shortcut.ipynb b/docs/notebooks/__ipynb__/widget_shortcut.ipynb new file mode 100644 index 00000000..7e4a2f48 --- /dev/null +++ b/docs/notebooks/__ipynb__/widget_shortcut.ipynb @@ -0,0 +1,193 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "Hbol", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "# The from_object shortcut" + ] + }, + { + "cell_type": "markdown", + "id": "MJUe", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 1. Import dependencies\n", + "\n", + "Import the functions and classes that we will be using." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vblA", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join\n", + "from urllib.request import urlretrieve\n", + "from anndata import read_h5ad\n", + "import scanpy as sc\n", + "\n", + "from vitessce import (\n", + " VitessceConfig,\n", + " Component as cm,\n", + " CoordinationType as ct,\n", + " AnnDataWrapper,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bkHC", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 2. Download the data\n", + "\n", + "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lEQa", + "metadata": {}, + "outputs": [], + "source": [ + "os.makedirs(\"data\", exist_ok=True)\n", + "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", + "urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" + ] + }, + { + "cell_type": "markdown", + "id": "PKri", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3. Load the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Xref", + "metadata": {}, + "outputs": [], + "source": [ + "adata = read_h5ad(join(\"data\", \"habib17.processed.h5ad\"))" + ] + }, + { + "cell_type": "markdown", + "id": "SFPL", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "## 3.1. Preprocess the Data For Visualization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "BYtC", + "metadata": {}, + "outputs": [], + "source": [ + "top_dispersion = adata.var[\"dispersions_norm\"][\n", + " sorted(\n", + " range(len(adata.var[\"dispersions_norm\"])),\n", + " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", + " )[-51:][0]\n", + "]\n", + "adata.var[\"top_highly_variable\"] = (\n", + " adata.var[\"dispersions_norm\"] > top_dispersion\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "RGSE", + "metadata": { + "marimo": { + "config": { + "hide_code": true + } + } + }, + "source": [ + "With one line of code, you may create a Vitessce widget based on an automatically inferred configuration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "Kclp", + "metadata": {}, + "outputs": [], + "source": [ + "vw = VitessceConfig.from_object(AnnDataWrapper(\n", + " adata,\n", + " obs_embedding_paths=[\"obsm/X_umap\"],\n", + " obs_embedding_names=[\"UMAP\"],\n", + " obs_set_paths=[\"obs/CellType\"],\n", + " obs_set_names=[\"Cell Type\"],\n", + " obs_feature_matrix_path=\"X\",\n", + " feature_filter_path=\"var/top_highly_variable\"\n", + "), schema_version=\"1.0.15\").widget(height=800)\n", + "vw" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/notebooks/cellbrowser_to_vitessce_config_conversion.ipynb b/docs/notebooks/cellbrowser_to_vitessce_config_conversion.ipynb deleted file mode 100644 index 3ca1e0cf..00000000 --- a/docs/notebooks/cellbrowser_to_vitessce_config_conversion.ipynb +++ /dev/null @@ -1,217 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "710bc947", - "metadata": {}, - "source": [ - "# Load UCSC Cell Browser project in Vitessce" - ] - }, - { - "cell_type": "markdown", - "id": "fad939f6-bd8b-46f8-8dd1-f0816d8ca5b3", - "metadata": {}, - "source": [ - "This notebook shows you how to use the `convert_cell_browser_project_to_anndata` function, which allows you to take an existing project, published in https://cells.ucsc.edu/ and:\n", - "1. Convert it into the AnnData format that is supported by Vitessce\n", - "2. Save the AnnData object as a Zarr store\n", - "3. Configure Vitessce with the AnnData-Zarr store\n", - "4. Render a Vitessce widget based on the config (step 3) directly in the notebook.\n", - "\n", - "The dataset that you choose to convert needs to be a valid UCSC Cell Browser \"project\", accessible from https://cells.ucsc.edu/, with a configuration available in https://github.com/ucscGenomeBrowser/cellbrowser-confs\n", - "\n", - "The `convert_cell_browser_project_to_anndata` function takes the name of that project as an input. For example, to convert this project, https://cells.ucsc.edu/?ds=adultPancreas, you will neeed to pass `\"adultPancreas\"` as the project name." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "532fea6a-69d4-4cac-8afb-6d334dbe7ca1", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import json\n", - "from os.path import join\n", - "from vitessce import (\n", - " convert_cell_browser_project_to_anndata,\n", - " AnnDataWrapper,\n", - " VitessceConfig,\n", - ")\n", - "from vitessce.data_utils import VAR_CHUNK_SIZE" - ] - }, - { - "cell_type": "markdown", - "id": "a8077cfd-abc2-488d-9d91-83bc29a0bbe9", - "metadata": {}, - "source": [ - "## 1. Convert UCSC Cell Browser project to a format that is supported by Vitessce\n", - "#### Output:\n", - "An AnnData object\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "743c2d61-d98c-4e8d-a821-d5fe0ec2d93b", - "metadata": {}, - "outputs": [], - "source": [ - "## 3. Convert UCSC Cell Browser project to a Vitessce view config" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "6fb3e7dc-baf8-49e9-9d24-264bcd668b49", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Converting CellBrowser config for project adultPancreas to Anndata-Zarr object\n", - "Successfully fetched configuration: https://cells.ucsc.edu/adultPancreas/dataset.json.\n", - "CellBrowser config is valid. Proceeding further with conversion.\n", - "Downloading expression matrix ...\n", - "Successfully downloaded expression matrix https://cells.ucsc.edu/adultPancreas/exprMatrix.tsv.gz.\n", - "Loading expression matrix into Anndata object ...\n", - "This dataset uses the format identifier|symbol for the ad.obs gene names (e.g. “ENSG0123123.3|HOX3”). We are keeping only the symbol.\n", - "Adding cell metadata to Anndata object ...\n", - "Successfully downloaded metadata meta.tsv.\n", - "Successful extraction of the following coordinates and URLS: {'X_tsne': 'tMinusSNE.coords.tsv.gz'}\n", - "Adding X_tsne to Anndata object ...\n", - "X_tsne successfully added.\n", - "Done adding coordinates to the Anndata object.\n", - "Filtering out all non-marker genes from Anndata object ...\n", - "Successfully filtered out all non-marker genes from Anndata object.\n", - "About to write the Anndata object to the Zarr store. The following properties will be saved:\n", - " Obs columns: ['cluster', 'age', 'age_unit', 'Key', 'experiment_name', 'fragAnalyzerRange', 'nCells', 'ng_ul', 'plate_nr', 'sample_recieve_date', 'chip_type', 'c1_chip_id', 'enrichment_method', 'capture_position', 'gene_body_coverage', 'intron_exon_ratio', 'mapped_reads', 'total_reads', 'n_genes']\n", - " Obsm keys: ['X_tsne']\n", - " Var columns: ['gene', 'n_cells']\n", - "obsm X_tsne is an instance of DataFrame, converting it to numpy array.\n" - ] - } - ], - "source": [ - "# Example run, coverting \"adultPancreas\" project:\n", - "adata = convert_cell_browser_project_to_anndata(project_name=\"adultPancreas\", keep_only_marker_genes=True)" - ] - }, - { - "cell_type": "markdown", - "id": "cf3cfcbe-4048-4a60-8988-b8c0eace23e2", - "metadata": {}, - "source": [ - "## 2. Save the AnnData object as a Zarr store" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "8835ab53-2ee3-490e-a68c-c2d8952277a9", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "zarr_filepath = join(\"data\", \"out.adata.zarr\")\n", - "os.makedirs(os.path.dirname(zarr_filepath), exist_ok=True)\n", - "adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "markdown", - "id": "d61667b4-dc32-4376-bff1-b4a5bf74140f", - "metadata": {}, - "source": [ - "## 3. Configure Vitessce with the AnnData-Zarr store" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "259c1804-2e67-4a92-bc90-5ba5e3dba7b3", - "metadata": {}, - "outputs": [], - "source": [ - "anndata_wrapper_inst = AnnDataWrapper(\n", - " adata_path=zarr_filepath,\n", - " obs_feature_matrix_path=\"X\",\n", - " obs_embedding_paths=[\"obsm/X_tsne\"],\n", - " obs_embedding_names=[\"t-SNE\"],\n", - " obs_set_paths=[\"obs/cluster\", \"obs/age\"],\n", - " obs_set_names=[\"cluster\", \"age\"],\n", - ")\n", - "vc = VitessceConfig(schema_version=\"1.0.15\", name=\"Vitessce configuration for CellBrowser project adultPancreas\")\n", - "anndata_wrapper_inst.auto_view_config(vc)" - ] - }, - { - "cell_type": "markdown", - "id": "22e7d2fd-2c2e-4ce5-b551-7809cdc6568e", - "metadata": {}, - "source": [ - "## 4. Render the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "cb9cb8e3-8ef4-49d9-b0a0-ba2f0fc80637", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e5878bf30e1f4428a14604731928972d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VitessceWidget(config={'version': '1.0.15', 'name': 'Vitessce configuration for CellBrowser project adultPancr…" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "774b8156-5cc6-4d17-884b-595957366230", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.9.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/notebooks/cellbrowser_to_vitessce_config_conversion.mo.py b/docs/notebooks/cellbrowser_to_vitessce_config_conversion.mo.py new file mode 100644 index 00000000..a4ffe018 --- /dev/null +++ b/docs/notebooks/cellbrowser_to_vitessce_config_conversion.mo.py @@ -0,0 +1,143 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Load UCSC Cell Browser project in Vitessce + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + This notebook shows you how to use the `convert_cell_browser_project_to_anndata` function, which allows you to take an existing project, published in https://cells.ucsc.edu/ and: + 1. Convert it into the AnnData format that is supported by Vitessce + 2. Save the AnnData object as a Zarr store + 3. Configure Vitessce with the AnnData-Zarr store + 4. Render a Vitessce widget based on the config (step 3) directly in the notebook. + + The dataset that you choose to convert needs to be a valid UCSC Cell Browser "project", accessible from https://cells.ucsc.edu/, with a configuration available in https://github.com/ucscGenomeBrowser/cellbrowser-confs + + The `convert_cell_browser_project_to_anndata` function takes the name of that project as an input. For example, to convert this project, https://cells.ucsc.edu/?ds=adultPancreas, you will neeed to pass `"adultPancreas"` as the project name. + """ + ) + return + + +@app.cell +def _(): + import os + import json + from os.path import join + from vitessce import ( + convert_cell_browser_project_to_anndata, + AnnDataWrapper, + VitessceConfig, + ) + from vitessce.data_utils import VAR_CHUNK_SIZE + return ( + AnnDataWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + convert_cell_browser_project_to_anndata, + join, + os, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Convert UCSC Cell Browser project to a format that is supported by Vitessce + #### Output: + An AnnData object + + """ + ) + return + + +@app.cell +def _(convert_cell_browser_project_to_anndata): + # Example run, coverting "adultPancreas" project: + adata = convert_cell_browser_project_to_anndata(project_name="adultPancreas", keep_only_marker_genes=True) + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Save the AnnData object as a Zarr store + """ + ) + return + + +@app.cell +def _(VAR_CHUNK_SIZE, adata, join, os): + zarr_filepath = join("data", "out.adata.zarr") + os.makedirs(os.path.dirname(zarr_filepath), exist_ok=True) + adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE]) + return (zarr_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Configure Vitessce with the AnnData-Zarr store + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, zarr_filepath): + anndata_wrapper_inst = AnnDataWrapper( + adata_path=zarr_filepath, + obs_feature_matrix_path="X", + obs_embedding_paths=["obsm/X_tsne"], + obs_embedding_names=["t-SNE"], + obs_set_paths=["obs/cluster", "obs/age"], + obs_set_names=["cluster", "age"], + ) + vc = VitessceConfig(schema_version="1.0.15", name="Vitessce configuration for CellBrowser project adultPancreas") + anndata_wrapper_inst.auto_view_config(vc) + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Render the Vitessce widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/config_to_python.ipynb b/docs/notebooks/config_to_python.ipynb deleted file mode 100644 index ed5e2d89..00000000 --- a/docs/notebooks/config_to_python.ipynb +++ /dev/null @@ -1,184 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "10f1c457-d0c8-4630-8e49-45df13b9c6d0", - "metadata": {}, - "source": [ - "# Generate Python code to reconstruct a VitessceConfig instance" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "67d5193a", - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import VitessceConfig, VitessceChainableConfig, VitessceConfigDatasetFile" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "721dacde", - "metadata": {}, - "outputs": [], - "source": [ - "from example_configs import dries as dries_config" - ] - }, - { - "cell_type": "markdown", - "id": "0954be13-6157-4e72-871d-4b1cd6b36ff0", - "metadata": {}, - "source": [ - "## Load a view config from a dict representation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "61000b61", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "vc = VitessceConfig.from_dict(dries_config)" - ] - }, - { - "cell_type": "markdown", - "id": "0999e895-194e-4890-9cf7-b7c7cc60c9ee", - "metadata": {}, - "source": [ - "## Print to JSON" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f777b428-4d75-487e-b08d-9d66f30fbe9a", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "print(json.dumps(vc.to_dict(), indent=2))" - ] - }, - { - "cell_type": "markdown", - "id": "6b51dc66-096c-4c69-a041-62c5bdd523e7", - "metadata": {}, - "source": [ - "## Print to Python\n", - "\n", - "The `vc.to_python` function generates formatted Python code which can be used to re-generate the `vc` instance." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15ae4ca3-490e-4b99-ae4d-8fafedf60885", - "metadata": {}, - "outputs": [], - "source": [ - "imports, code = vc.to_python()" - ] - }, - { - "cell_type": "markdown", - "id": "619a87f0-9eff-49c0-a092-7859c2a54d16", - "metadata": {}, - "source": [ - "The first value returned is a list of classes used by the code snippet." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6ec458e3-1970-42ab-876c-cc7407d9cc19", - "metadata": {}, - "outputs": [], - "source": [ - "imports" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0a458da5-9fe1-45fa-b6d5-a09acb3493d3", - "metadata": {}, - "outputs": [], - "source": [ - "print(code)" - ] - }, - { - "cell_type": "markdown", - "id": "e47433f0-0b65-4204-becb-ec9b0c59b731", - "metadata": {}, - "source": [ - "The second value is the code snippet. When evaluated, the result will be a new `VitessceConfig` instance." - ] - }, - { - "cell_type": "markdown", - "id": "a82a7938-923c-4947-bdf9-49d1f30134f6", - "metadata": {}, - "source": [ - "## Evaluate the code and render a Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "94434a43", - "metadata": {}, - "outputs": [], - "source": [ - "reconstructed_vc = eval(code)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf0110ff", - "metadata": {}, - "outputs": [], - "source": [ - "reconstructed_vc.widget()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "96bf94ce-1963-4ec3-8661-178e6adbbcb7", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/notebooks/config_to_python.mo.py b/docs/notebooks/config_to_python.mo.py new file mode 100644 index 00000000..a9d825bc --- /dev/null +++ b/docs/notebooks/config_to_python.mo.py @@ -0,0 +1,117 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App(width="medium") + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""# Generate Python code to reconstruct a VitessceConfig instance""") + return + + +@app.cell +def _(): + from vitessce import VitessceConfig, VitessceChainableConfig, VitessceConfigDatasetFile + return (VitessceConfig,) + + +@app.cell +def _(): + from example_configs import dries as dries_config + return (dries_config,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Load a view config from a dict representation""") + return + + +@app.cell +def _(VitessceConfig, dries_config): + vc = VitessceConfig.from_dict(dries_config) + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Print to JSON""") + return + + +@app.cell +def _(vc): + import json + print(json.dumps(vc.to_dict(), indent=2)) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Print to Python + + The `vc.to_python` function generates formatted Python code which can be used to re-generate the `vc` instance. + """ + ) + return + + +@app.cell +def _(vc): + imports, code = vc.to_python() + return code, imports + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""The first value returned is a list of classes used by the code snippet.""") + return + + +@app.cell +def _(imports): + imports + return + + +@app.cell +def _(code): + print(code) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""The second value is the code snippet. When evaluated, the result will be a new `VitessceConfig` instance.""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Evaluate the code and render a Vitessce widget""") + return + + +@app.cell +def _(code): + reconstructed_vc = eval(code) + return (reconstructed_vc,) + + +@app.cell +def _(reconstructed_vc): + reconstructed_vc.widget() + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/data_conversion.ipynb b/docs/notebooks/data_conversion.ipynb deleted file mode 100644 index 69f3f66f..00000000 --- a/docs/notebooks/data_conversion.ipynb +++ /dev/null @@ -1,103 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Data Preparation Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Convert data manually" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When running the Vitessce widget, data is converted on-the-fly into formats that the Vitessce JavaScript component can render.\n", - "The converted data is stored in temporary files, preventing long-term use of the converted files.\n", - "\n", - "However, the data conversion utilities used by the widget are exposed so that their outputs can be saved to regular files.\n", - "This allows the files to be saved for future use with the Vitessce web application by serving the files locally or moving the files onto an object storage system such as AWS S3 (for long-term storage and data sharing).\n", - "\n", - "This notebook demonstrates how to save the processed outputs of the `AnnDataWrapper` and `SnapWrapper` classes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import SnapWrapper\n", - "from os.path import join\n", - "from scipy.io import mmread\n", - "import pandas as pd\n", - "import numpy as np\n", - "import json" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mtx = mmread(join('data', 'snapatac', 'filtered_cell_by_bin.mtx'))\n", - "barcodes_df = pd.read_csv(join('data', 'snapatac', 'barcodes.txt'), header=None)\n", - "bins_df = pd.read_csv(join('data', 'snapatac', 'bins.txt'), header=None)\n", - "clusters_df = pd.read_csv(join('data', 'snapatac', 'umap_coords_clusters.csv'), index_col=0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zarr_filepath = join('data', 'snapatac', 'out.snap.multires.zarr')\n", - "\n", - "w = SnapWrapper(mtx, barcodes_df, bins_df, clusters_df)\n", - "\n", - "cells_json = w.create_cells_json()\n", - "cell_sets_json = w.create_cell_sets_json()\n", - "\n", - "with open(join('data', 'snapatac', 'out.cells.json'), 'w') as f:\n", - " json.dump(cells_json, f)\n", - "\n", - "with open(join('data', 'snapatac', 'out.cell-sets.json'), 'w') as f:\n", - " json.dump(cell_sets_json, f)\n", - "\n", - "\n", - "w.create_genomic_multivec_zarr(zarr_filepath)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/data_conversion.mo.py b/docs/notebooks/data_conversion.mo.py new file mode 100644 index 00000000..956bcf45 --- /dev/null +++ b/docs/notebooks/data_conversion.mo.py @@ -0,0 +1,80 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Convert data manually + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + When running the Vitessce widget, data is converted on-the-fly into formats that the Vitessce JavaScript component can render. + The converted data is stored in temporary files, preventing long-term use of the converted files. + + However, the data conversion utilities used by the widget are exposed so that their outputs can be saved to regular files. + This allows the files to be saved for future use with the Vitessce web application by serving the files locally or moving the files onto an object storage system such as AWS S3 (for long-term storage and data sharing). + + This notebook demonstrates how to save the processed outputs of the `AnnDataWrapper` and `SnapWrapper` classes. + """ + ) + return + + +@app.cell +def _(): + from vitessce import SnapWrapper + from os.path import join + from scipy.io import mmread + import pandas as pd + import numpy as np + import json + return SnapWrapper, join, json, mmread, pd + + +@app.cell +def _(join, mmread, pd): + mtx = mmread(join('data', 'snapatac', 'filtered_cell_by_bin.mtx')) + barcodes_df = pd.read_csv(join('data', 'snapatac', 'barcodes.txt'), header=None) + bins_df = pd.read_csv(join('data', 'snapatac', 'bins.txt'), header=None) + clusters_df = pd.read_csv(join('data', 'snapatac', 'umap_coords_clusters.csv'), index_col=0) + return barcodes_df, bins_df, clusters_df, mtx + + +@app.cell +def _(SnapWrapper, barcodes_df, bins_df, clusters_df, join, json, mtx): + zarr_filepath = join('data', 'snapatac', 'out.snap.multires.zarr') + + w = SnapWrapper(mtx, barcodes_df, bins_df, clusters_df) + + cells_json = w.create_cells_json() + cell_sets_json = w.create_cell_sets_json() + + with open(join('data', 'snapatac', 'out.cells.json'), 'w') as f: + json.dump(cells_json, f) + + with open(join('data', 'snapatac', 'out.cell-sets.json'), 'w') as f: + json.dump(cell_sets_json, f) + + + w.create_genomic_multivec_zarr(zarr_filepath) + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/data_export_files.ipynb b/docs/notebooks/data_export_files.ipynb deleted file mode 100644 index d2b17c6e..00000000 --- a/docs/notebooks/data_export_files.ipynb +++ /dev/null @@ -1,236 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Data Preparation Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Export data to local files" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import json\n", - "from urllib.parse import quote_plus\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "\n", - "from vitessce import (\n", - " VitessceWidget,\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")\n", - "from vitessce.data_utils import (\n", - " optimize_adata,\n", - " VAR_CHUNK_SIZE,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Download and process data\n", - "\n", - "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", - "if not isfile(adata_filepath):\n", - " os.makedirs(\"data\", exist_ok=True)\n", - " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)\n", - "\n", - "adata = read_h5ad(adata_filepath)\n", - "top_dispersion = adata.var[\"dispersions_norm\"][\n", - " sorted(\n", - " range(len(adata.var[\"dispersions_norm\"])),\n", - " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", - " )[-51:][0]\n", - "]\n", - "adata.var[\"top_highly_variable\"] = (\n", - " adata.var[\"dispersions_norm\"] > top_dispersion\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zarr_filepath = join(\"data\", \"habib17.processed.zarr\")\n", - "if not isdir(zarr_filepath):\n", - " adata = optimize_adata(\n", - " adata,\n", - " obs_cols=[\"CellType\"],\n", - " obsm_keys=[\"X_umap\"],\n", - " var_cols=[\"top_highly_variable\"],\n", - " optimize_X=True,\n", - " )\n", - " adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Create the Vitessce configuration" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Set up the configuration by adding the views and datasets of interest." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', description='COVID-19 Healthy Donor Brain')\n", - "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", - " adata_path=zarr_filepath,\n", - " obs_embedding_paths=[\"obsm/X_umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/CellType\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_feature_matrix_path=\"X\",\n", - " feature_filter_path=\"var/top_highly_variable\"\n", - "))\n", - "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"X_umap\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)\n", - "vc.layout((scatterplot | (cell_sets / genes)) / heatmap);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Export files to a local directory\n", - "\n", - "The `.export(to='files')` method on the view config instance will export files to the specified directory `out_dir`. The `base_url` parameter is required so that the file URLs in the view config point to the location where you ultimately intend to serve the files." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "config_dict = vc.export(to='files', base_url='http://localhost:3000', out_dir='./test')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Serve the files" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that the files have been saved to the `./test` directory, they can be served by any static web server.\n", - "\n", - "If you would like to serve the files locally, we recommend [http-server](https://github.com/http-party/http-server) which can be installed with NPM or Homebrew:\n", - "```sh\n", - "cd test\n", - "http-server ./ --cors -p 3000\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. View on vitessce.io\n", - "\n", - "The returned view config dict can be converted to a URL, and if the files are served on the internet (rather than locally), this URL can be used to share the interactive visualizations with colleagues." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vitessce_url = \"http://vitessce.io/?url=data:,\" + quote_plus(json.dumps(config_dict))\n", - "import webbrowser\n", - "webbrowser.open(vitessce_url)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/data_export_files.mo.py b/docs/notebooks/data_export_files.mo.py new file mode 100644 index 00000000..ad827c86 --- /dev/null +++ b/docs/notebooks/data_export_files.mo.py @@ -0,0 +1,219 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Export data to local files + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + import os + import json + from urllib.parse import quote_plus + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + + from vitessce import ( + VitessceWidget, + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + from vitessce.data_utils import ( + optimize_adata, + VAR_CHUNK_SIZE, + ) + return ( + AnnDataWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + cm, + isdir, + isfile, + join, + json, + optimize_adata, + os, + quote_plus, + read_h5ad, + urlretrieve, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Download and process data + + For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17. + """ + ) + return + + +@app.cell +def _(isfile, join, os, read_h5ad, urlretrieve): + adata_filepath = join("data", "habib17.processed.h5ad") + if not isfile(adata_filepath): + os.makedirs("data", exist_ok=True) + urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath) + + adata = read_h5ad(adata_filepath) + top_dispersion = adata.var["dispersions_norm"][ + sorted( + range(len(adata.var["dispersions_norm"])), + key=lambda k: adata.var["dispersions_norm"][k], + )[-51:][0] + ] + adata.var["top_highly_variable"] = ( + adata.var["dispersions_norm"] > top_dispersion + ) + return (adata,) + + +@app.cell +def _(VAR_CHUNK_SIZE, adata, isdir, join, optimize_adata): + zarr_filepath = join('data', 'habib17.processed.zarr') + if not isdir(zarr_filepath): + adata_1 = optimize_adata(adata, obs_cols=['CellType'], obsm_keys=['X_umap'], var_cols=['top_highly_variable'], optimize_X=True) + adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE]) + return (zarr_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Create the Vitessce configuration + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Set up the configuration by adding the views and datasets of interest. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, cm, zarr_filepath): + vc = VitessceConfig(schema_version="1.0.15", name='Habib et al', description='COVID-19 Healthy Donor Brain') + dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper( + adata_path=zarr_filepath, + obs_embedding_paths=["obsm/X_umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/CellType"], + obs_set_names=["Cell Type"], + obs_feature_matrix_path="X", + feature_filter_path="var/top_highly_variable" + )) + scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="X_umap") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + heatmap = vc.add_view(cm.HEATMAP, dataset=dataset) + vc.layout((scatterplot | (cell_sets / genes)) / heatmap); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Export files to a local directory + + The `.export(to='files')` method on the view config instance will export files to the specified directory `out_dir`. The `base_url` parameter is required so that the file URLs in the view config point to the location where you ultimately intend to serve the files. + """ + ) + return + + +@app.cell +def _(vc): + config_dict = vc.export(to='files', base_url='http://localhost:3000', out_dir='./test') + return (config_dict,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Serve the files + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Now that the files have been saved to the `./test` directory, they can be served by any static web server. + + If you would like to serve the files locally, we recommend [http-server](https://github.com/http-party/http-server) which can be installed with NPM or Homebrew: + ```sh + cd test + http-server ./ --cors -p 3000 + ``` + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 6. View on vitessce.io + + The returned view config dict can be converted to a URL, and if the files are served on the internet (rather than locally), this URL can be used to share the interactive visualizations with colleagues. + """ + ) + return + + +@app.cell +def _(config_dict, json, quote_plus): + vitessce_url = "http://vitessce.io/?url=data:," + quote_plus(json.dumps(config_dict)) + import webbrowser + webbrowser.open(vitessce_url) + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/data_export_s3.ipynb b/docs/notebooks/data_export_s3.ipynb deleted file mode 100644 index c84a0e43..00000000 --- a/docs/notebooks/data_export_s3.ipynb +++ /dev/null @@ -1,230 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Data Preparation Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Export data to AWS S3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import boto3\n", - "import json\n", - "from urllib.parse import quote_plus\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "\n", - "from vitessce import (\n", - " VitessceWidget,\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")\n", - "from vitessce.data_utils import (\n", - " optimize_adata,\n", - " VAR_CHUNK_SIZE,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Download and process data\n", - "\n", - "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", - "if not isfile(adata_filepath):\n", - " os.makedirs(\"data\", exist_ok=True)\n", - " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)\n", - "\n", - "adata = read_h5ad(adata_filepath)\n", - "top_dispersion = adata.var[\"dispersions_norm\"][\n", - " sorted(\n", - " range(len(adata.var[\"dispersions_norm\"])),\n", - " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", - " )[-51:][0]\n", - "]\n", - "adata.var[\"top_highly_variable\"] = (\n", - " adata.var[\"dispersions_norm\"] > top_dispersion\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zarr_filepath = join(\"data\", \"habib17.processed.zarr\")\n", - "if not isdir(zarr_filepath):\n", - " adata = optimize_adata(\n", - " adata,\n", - " obs_cols=[\"CellType\"],\n", - " obsm_keys=[\"X_umap\"],\n", - " var_cols=[\"top_highly_variable\"],\n", - " optimize_X=True,\n", - " )\n", - " adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Create the Vitessce configuration" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Set up the configuration by adding the views and datasets of interest." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', description='COVID-19 Healthy Donor Brain')\n", - "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", - " adata_path=zarr_filepath,\n", - " obs_embedding_paths=[\"obsm/X_umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/CellType\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_feature_matrix_path=\"X\",\n", - " feature_filter_path=\"var/top_highly_variable\"\n", - "))\n", - "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)\n", - "vc.layout((scatterplot | (cell_sets / genes)) / heatmap);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Create a `boto3` resource with S3 credentials" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s3 = boto3.resource(\n", - " service_name='s3',\n", - " aws_access_key_id=os.environ['VITESSCE_S3_ACCESS_KEY_ID'],\n", - " aws_secret_access_key=os.environ['VITESSCE_S3_SECRET_ACCESS_KEY'],\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Upload files to S3\n", - "\n", - "The `.export(to='S3')` method on the view config instance will upload all data objects to the specified bucket. Then, the processed view config will be returned as a `dict`, with the file URLs filled in, pointing to the S3 bucket files. For more information about configuring the S3 bucket so that files are accessible over the internet, visit the \"Hosting Data\" page of our core documentation site." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "config_dict = vc.export(to='S3', s3=s3, bucket_name='vitessce-export-examples', prefix='test')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. View on vitessce.io\n", - "\n", - "The returned view config dict can be converted to a URL, and can be used to share the interactive visualizations with colleagues." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vitessce_url = \"http://vitessce.io/?url=data:,\" + quote_plus(json.dumps(config_dict))\n", - "import webbrowser\n", - "webbrowser.open(vitessce_url)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/data_export_s3.mo.py b/docs/notebooks/data_export_s3.mo.py new file mode 100644 index 00000000..a070ae92 --- /dev/null +++ b/docs/notebooks/data_export_s3.mo.py @@ -0,0 +1,215 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Export data to AWS S3 + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + import os + import boto3 + import json + from urllib.parse import quote_plus + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + + from vitessce import ( + VitessceWidget, + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + from vitessce.data_utils import ( + optimize_adata, + VAR_CHUNK_SIZE, + ) + return ( + AnnDataWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + boto3, + cm, + isdir, + isfile, + join, + json, + optimize_adata, + os, + quote_plus, + read_h5ad, + urlretrieve, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Download and process data + + For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17. + """ + ) + return + + +@app.cell +def _(isfile, join, os, read_h5ad, urlretrieve): + adata_filepath = join("data", "habib17.processed.h5ad") + if not isfile(adata_filepath): + os.makedirs("data", exist_ok=True) + urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath) + + adata = read_h5ad(adata_filepath) + top_dispersion = adata.var["dispersions_norm"][ + sorted( + range(len(adata.var["dispersions_norm"])), + key=lambda k: adata.var["dispersions_norm"][k], + )[-51:][0] + ] + adata.var["top_highly_variable"] = ( + adata.var["dispersions_norm"] > top_dispersion + ) + return (adata,) + + +@app.cell +def _(VAR_CHUNK_SIZE, adata, isdir, join, optimize_adata): + zarr_filepath = join('data', 'habib17.processed.zarr') + if not isdir(zarr_filepath): + adata_1 = optimize_adata(adata, obs_cols=['CellType'], obsm_keys=['X_umap'], var_cols=['top_highly_variable'], optimize_X=True) + adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE]) + return (zarr_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Create the Vitessce configuration + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Set up the configuration by adding the views and datasets of interest. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, cm, zarr_filepath): + vc = VitessceConfig(schema_version="1.0.15", name='Habib et al', description='COVID-19 Healthy Donor Brain') + dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper( + adata_path=zarr_filepath, + obs_embedding_paths=["obsm/X_umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/CellType"], + obs_set_names=["Cell Type"], + obs_feature_matrix_path="X", + feature_filter_path="var/top_highly_variable" + )) + scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + heatmap = vc.add_view(cm.HEATMAP, dataset=dataset) + vc.layout((scatterplot | (cell_sets / genes)) / heatmap); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Create a `boto3` resource with S3 credentials + """ + ) + return + + +@app.cell +def _(boto3, os): + s3 = boto3.resource( + service_name='s3', + aws_access_key_id=os.environ['VITESSCE_S3_ACCESS_KEY_ID'], + aws_secret_access_key=os.environ['VITESSCE_S3_SECRET_ACCESS_KEY'], + ) + return (s3,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Upload files to S3 + + The `.export(to='S3')` method on the view config instance will upload all data objects to the specified bucket. Then, the processed view config will be returned as a `dict`, with the file URLs filled in, pointing to the S3 bucket files. For more information about configuring the S3 bucket so that files are accessible over the internet, visit the "Hosting Data" page of our core documentation site. + """ + ) + return + + +@app.cell +def _(s3, vc): + config_dict = vc.export(to='S3', s3=s3, bucket_name='vitessce-export-examples', prefix='test') + return (config_dict,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 6. View on vitessce.io + + The returned view config dict can be converted to a URL, and can be used to share the interactive visualizations with colleagues. + """ + ) + return + + +@app.cell +def _(config_dict, json, quote_plus): + vitessce_url = "http://vitessce.io/?url=data:," + quote_plus(json.dumps(config_dict)) + import webbrowser + webbrowser.open(vitessce_url) + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/marimo-wasm.py b/docs/notebooks/marimo-wasm.mo.py similarity index 96% rename from docs/notebooks/marimo-wasm.py rename to docs/notebooks/marimo-wasm.mo.py index 78c82ace..4ec0806c 100644 --- a/docs/notebooks/marimo-wasm.py +++ b/docs/notebooks/marimo-wasm.mo.py @@ -68,13 +68,13 @@ def _(mo): @app.cell def _(vw): - vw.config + vw._config return @app.cell def _(vw): - vw.config["coordinationSpace"]["spatialZoom"] + vw._config["coordinationSpace"]["spatialZoom"] return @app.cell diff --git a/docs/notebooks/marimo.py b/docs/notebooks/marimo.mo.py similarity index 100% rename from docs/notebooks/marimo.py rename to docs/notebooks/marimo.mo.py diff --git a/docs/notebooks/page_mode_comparative.ipynb b/docs/notebooks/page_mode_comparative.ipynb deleted file mode 100644 index 49607f86..00000000 --- a/docs/notebooks/page_mode_comparative.ipynb +++ /dev/null @@ -1,490 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of ComparativeData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from oxc_py import transform\n", - "from vitessce import VitessceConfig, hconcat, vconcat" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure the data and views" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Reference: https://github.com/vitessce/vitessce/blob/main/examples/configs/src/view-configs/kpmp-premiere.js" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "base_url = 'https://storage.googleapis.com/vitessce-demo-data/kpmp-jan-2025/kpmp_premiere_20250330.adata.zarr'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.17\", name='Lake et al.')\n", - "\n", - "dataset = vc.add_dataset('lake_et_al').add_file(\n", - " file_type='comparisonMetadata.anndata.zarr',\n", - " url=base_url,\n", - " options={\n", - " \"path\": 'uns/comparison_metadata',\n", - " },\n", - " coordination_values={\n", - " \"obsType\": 'cell',\n", - " \"sampleType\": 'sample',\n", - " },\n", - ").add_file(\n", - " file_type='comparativeFeatureStats.anndata.zarr',\n", - " url=base_url,\n", - " options= {\n", - " \"metadataPath\": 'uns/comparison_metadata',\n", - " \"indexColumn\": 'names',\n", - " \"pValueColumn\": 'pvals_adj',\n", - " \"foldChangeColumn\": 'logfoldchanges',\n", - " \"pValueAdjusted\": True,\n", - " \"foldChangeTransformation\": 'log2',\n", - " },\n", - " coordination_values={\n", - " \"obsType\": 'cell',\n", - " \"sampleType\": 'sample',\n", - " \"featureType\": 'gene',\n", - " },\n", - ").add_file(\n", - " file_type= 'comparativeObsSetStats.anndata.zarr',\n", - " url= base_url,\n", - " options= {\n", - " \"metadataPath\": 'uns/comparison_metadata',\n", - " \"indexColumn\": 'Cell Type',\n", - " \"interceptExpectedSampleColumn\": 'Expected Sample_intercept',\n", - " \"effectExpectedSampleColumn\": 'Expected Sample_effect',\n", - " \"foldChangeColumn\": 'log2-fold change',\n", - " \"foldChangeTransformation\": 'log2',\n", - " \"isCredibleEffectColumn\": 'is_credible_effect',\n", - " },\n", - " coordination_values= {\n", - " \"obsType\": 'cell',\n", - " \"sampleType\": 'sample',\n", - " },\n", - ").add_file(\n", - " file_type='comparativeFeatureSetStats.anndata.zarr',\n", - " url=base_url,\n", - " options= {\n", - " \"metadataPath\": 'uns/comparison_metadata',\n", - " \"indexColumn\": 'pathway_name',\n", - " \"termColumn\": 'pathway_term',\n", - " \"pValueColumn\": 'pvals_adj',\n", - " \"pValueAdjusted\": True,\n", - " \"analysisType\": 'pertpy_hypergeometric',\n", - " \"featureSetLibrary\": 'Reactome_2022',\n", - " },\n", - " coordination_values= {\n", - " \"obsType\": 'cell',\n", - " \"featureType\": 'gene',\n", - " \"sampleType\": 'sample',\n", - " },\n", - ").add_file(\n", - " file_type='anndata.zarr',\n", - " url=base_url,\n", - " coordination_values={\n", - " \"obsType\": 'cell',\n", - " \"featureType\": 'gene',\n", - " \"featureValueType\": 'expression',\n", - " \"sampleType\": 'sample',\n", - " },\n", - " options={\n", - " \"obsFeatureMatrix\": {\n", - " \"path\": 'layers/pearson_residuals',\n", - " },\n", - " \"obsEmbedding\": [\n", - " {\n", - " \"path\": 'obsm/X_densmap',\n", - " \"embeddingType\": 'densMAP',\n", - " },\n", - " ],\n", - " \"obsSets\": [\n", - " {\n", - " \"name\": 'Cell Type',\n", - " \"path\": 'obs/cell_type',\n", - " },\n", - " {\n", - " \"name\": 'Subclass L1',\n", - " \"path\": 'obs/subclass_l1',\n", - " },\n", - " {\n", - " \"name\": 'Subclass L2',\n", - " \"path\": 'obs/subclass_l2',\n", - " },\n", - " {\n", - " \"name\": 'Donor ID',\n", - " \"path\": 'obs/donor_id',\n", - " },\n", - " ],\n", - " \"sampleEdges\": {\n", - " \"path\": 'obs/SampleID',\n", - " },\n", - " },\n", - ").add_file(\n", - " file_type='sampleSets.anndata.zarr',\n", - " url=f\"{base_url}/uns/__all__.samples\",\n", - " options={\n", - " \"sampleSets\": [\n", - " {\n", - " \"name\": 'Disease Type',\n", - " \"path\": 'diseasetype',\n", - " },\n", - " {\n", - " \"name\": 'Adjudicated Category',\n", - " \"path\": 'AdjudicatedCategory',\n", - " },\n", - " {\n", - " \"name\": 'Enrollment Category',\n", - " \"path\": 'EnrollmentCategory',\n", - " },\n", - " ],\n", - " },\n", - " coordination_values= {\n", - " \"sampleType\": 'sample',\n", - " },\n", - ")\n", - "\n", - "biomarkerSelect = vc.add_view('biomarkerSelect', dataset=dataset, uid='biomarker-select')\n", - "comparativeHeading = vc.add_view('comparativeHeading', dataset=dataset, uid='comparative-heading')\n", - "dualScatterplot = vc.add_view('dualScatterplot', dataset=dataset, uid='scatterplot')\n", - "obsSets = vc.add_view('obsSets', dataset=dataset, uid='cell-sets')\n", - "sampleSets = vc.add_view('sampleSetPairManager', dataset=dataset, uid='sample-sets')\n", - "obsSetSizes = vc.add_view('obsSetSizes', dataset=dataset)\n", - "featureList = vc.add_view('featureList', dataset=dataset)\n", - "violinPlots = vc.add_view('obsSetFeatureValueDistribution', dataset=dataset, uid='violin-plot')\n", - "dotPlot = vc.add_view('dotPlot', dataset=dataset, uid='dot-plot')\n", - "treemap = vc.add_view('treemap', dataset=dataset, uid='treemap')\n", - "volcanoPlot = vc.add_view('volcanoPlot', dataset=dataset, uid='volcano-plot')\n", - "volcanoPlotTable = vc.add_view('featureStatsTable', dataset=dataset, uid='volcano-plot-table')\n", - "obsSetCompositionBarPlot = vc.add_view('obsSetCompositionBarPlot', dataset=dataset, uid='sccoda-plot')\n", - "featureSetEnrichmentBarPlot = vc.add_view('featureSetEnrichmentBarPlot', dataset=dataset, uid='pathways-plot')\n", - "\n", - "[sampleSetScope_caseControl] = vc.add_coordination('sampleSetSelection')\n", - "sampleSetScope_caseControl.set_value([['Disease Type', 'CKD'], ['Disease Type', 'Reference']])\n", - "\n", - "[featureSelectionScope] = vc.add_coordination('featureSelection')\n", - "featureSelectionScope.set_value(['UMOD', 'NPHS2'])\n", - "\n", - "vc.link_views_by_dict([dualScatterplot], {\n", - " \"embeddingType\": 'densMAP',\n", - " \"embeddingContoursVisible\": True,\n", - " \"embeddingPointsVisible\": False,\n", - " \"embeddingObsSetLabelsVisible\": True,\n", - "}, meta=False);\n", - "\n", - "\n", - "vc.link_views([biomarkerSelect, dualScatterplot, obsSets, obsSetSizes, featureList, violinPlots, dotPlot, treemap, volcanoPlot, volcanoPlotTable, comparativeHeading, obsSetCompositionBarPlot, featureSetEnrichmentBarPlot, sampleSets], ['sampleType'], ['sample'])\n", - "\n", - "vc.link_views_by_dict([biomarkerSelect, dualScatterplot, obsSets, obsSetSizes, featureList, violinPlots, dotPlot, treemap, volcanoPlot, volcanoPlotTable, comparativeHeading, obsSetCompositionBarPlot, featureSetEnrichmentBarPlot, sampleSets], {\n", - " \"sampleSetSelection\": sampleSetScope_caseControl,\n", - " \"featureSelection\": featureSelectionScope,\n", - "}, meta=False)\n", - "\n", - "vc.link_views_by_dict([dualScatterplot, violinPlots, featureList, dotPlot], {\n", - " # \"featureSelection\": ['UMOD', 'NPHS2'], // , 'ENSG00000074803', 'ENSG00000164825'],\n", - " \"obsColorEncoding\": 'geneSelection',\n", - " \"featureValueColormap\": 'jet',\n", - " \"featureValueColormapRange\": [0, 0.25],\n", - " \"featureAggregationStrategy\": None,\n", - "}, meta=False)\n", - "\n", - "vc.layout(hconcat(\n", - " vconcat(dualScatterplot, biomarkerSelect, comparativeHeading, obsSets, obsSetSizes, featureList),\n", - " vconcat(treemap, featureSetEnrichmentBarPlot, violinPlots, dotPlot, obsSetCompositionBarPlot, sampleSets),\n", - " volcanoPlotTable,\n", - "));" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## Define the page layout" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "PAGE_ESM = transform(\"\"\"\n", - "import clsx from \"https://unpkg.com/clsx@1.1.1/dist/clsx.m.js\";\n", - "\n", - "function createPage(utilsForPages) {\n", - " const {\n", - " React,\n", - " usePageModeView,\n", - " } = utilsForPages;\n", - " function PageComponent(props) {\n", - " const BiomarkerSelect = usePageModeView('biomarker-select');\n", - " const ComparativeHeading = usePageModeView('comparative-heading');\n", - " const CellSets = usePageModeView('cell-sets');\n", - " const SampleSets = usePageModeView('sample-sets');\n", - " const DualScatterplot = usePageModeView('scatterplot');\n", - " const ViolinPlot = usePageModeView('violin-plot');\n", - " const DotPlot = usePageModeView('dot-plot');\n", - " const Treemap = usePageModeView('treemap');\n", - " const VolcanoPlot = usePageModeView('volcano-plot');\n", - " const VolcanoPlotTable = usePageModeView('volcano-plot-table');\n", - " const SccodaPlot = usePageModeView('sccoda-plot');\n", - " const PathwaysPlot = usePageModeView('pathways-plot');\n", - "\n", - " \n", - " return (\n", - " <>\n", - " \n", - "
\n", - "
\n", - "

Comparative visualization of single-cell atlas data

\n", - " \n", - "
\n", - "
\n", - "\n", - "
\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - "

This view contains a treemap visualization to communicate cell type composition in each of the selected sample groups.

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - "
\n", - "

This view displays the results of a cell type composition analysis performed using the ScCODA algorithm (Büttner et al. 2021). Cell types with significantly different composition between the selected sample groups are displayed opaque while not-signficant results are displayed with transparent bars. The single outlined bar denotes the automatically-selected reference cell type.

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - "
\n", - "

This view displays differential expression test results, performed using the rank_genes_groups function from Scanpy (Wolf et al. 2018) with method "wilcoxon".

The arrows on the bottom left and bottom right denote the direction of the effect. Click a point in the plot to select the corresponding gene.

Note that differential expression tests have been run for each cell type separately, so the each gene can appear multiple times (once per cell type). If there are too many points on the plot, cell types can be selected to filter the points.

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - "
\n", - "

This view displays differential expression test results in tabular form. Click a row in the table to select the corresponding gene.

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - "
\n", - "

This view displays gene set enrichment test results based on the differential expression results. Gene set enrichment tests have been performed using Reactome 2022 pathway gene sets from BlitzGSEA (Lachmann et al. 2022) via the hypergeometric function of Pertpy (Heumos et al. 2024).

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - "
\n", - "

This view contains contour scatterplots which display the results of a density-preserving dimensionality reduction (Narayan et al. 2021). Contour opacities correspond to the shown percentile thresholds.

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - "
\n", - "

This dot plot view displays gene expression values per cell type and sample group for the selected biomarkers.

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - "
\n", - "

This violin plot view displays gene expression values per cell type and sample group for the selected biomarker.

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - " {/*

Neighborhood-level representations

\n", - "

TODO

\n", - "

Segmented instance-level representations

\n", - "

TODO

\n", - "

Image-level representations

\n", - "

TODO

\n", - "

Participant-level representations

\n", - "

TODO

*/}\n", - "
\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "\n", - "
\n", - " \n", - " );\n", - " }\n", - " return PageComponent;\n", - "}\n", - "export default { createPage };\n", - "\"\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Render page as widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget(page_esm=PAGE_ESM, page_mode=True, height=4700, prevent_scroll=False)\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/page_mode_comparative.mo.py b/docs/notebooks/page_mode_comparative.mo.py new file mode 100644 index 00000000..6440a387 --- /dev/null +++ b/docs/notebooks/page_mode_comparative.mo.py @@ -0,0 +1,451 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of ComparativeData object + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + """ + ) + return + + +@app.cell +def _(): + from oxc_py import transform + from vitessce import VitessceConfig, hconcat, vconcat + return VitessceConfig, hconcat, transform, vconcat + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure the data and views + """ + ) + return + + +@app.cell +def _(): + # Reference: https://github.com/vitessce/vitessce/blob/main/examples/configs/src/view-configs/kpmp-premiere.js + return + + +@app.cell +def _(): + base_url = 'https://storage.googleapis.com/vitessce-demo-data/kpmp-jan-2025/kpmp_premiere_20250330.adata.zarr' + return (base_url,) + + +@app.cell +def _(VitessceConfig, base_url, hconcat, vconcat): + vc = VitessceConfig(schema_version="1.0.17", name='Lake et al.') + + dataset = vc.add_dataset('lake_et_al').add_file( + file_type='comparisonMetadata.anndata.zarr', + url=base_url, + options={ + "path": 'uns/comparison_metadata', + }, + coordination_values={ + "obsType": 'cell', + "sampleType": 'sample', + }, + ).add_file( + file_type='comparativeFeatureStats.anndata.zarr', + url=base_url, + options= { + "metadataPath": 'uns/comparison_metadata', + "indexColumn": 'names', + "pValueColumn": 'pvals_adj', + "foldChangeColumn": 'logfoldchanges', + "pValueAdjusted": True, + "foldChangeTransformation": 'log2', + }, + coordination_values={ + "obsType": 'cell', + "sampleType": 'sample', + "featureType": 'gene', + }, + ).add_file( + file_type= 'comparativeObsSetStats.anndata.zarr', + url= base_url, + options= { + "metadataPath": 'uns/comparison_metadata', + "indexColumn": 'Cell Type', + "interceptExpectedSampleColumn": 'Expected Sample_intercept', + "effectExpectedSampleColumn": 'Expected Sample_effect', + "foldChangeColumn": 'log2-fold change', + "foldChangeTransformation": 'log2', + "isCredibleEffectColumn": 'is_credible_effect', + }, + coordination_values= { + "obsType": 'cell', + "sampleType": 'sample', + }, + ).add_file( + file_type='comparativeFeatureSetStats.anndata.zarr', + url=base_url, + options= { + "metadataPath": 'uns/comparison_metadata', + "indexColumn": 'pathway_name', + "termColumn": 'pathway_term', + "pValueColumn": 'pvals_adj', + "pValueAdjusted": True, + "analysisType": 'pertpy_hypergeometric', + "featureSetLibrary": 'Reactome_2022', + }, + coordination_values= { + "obsType": 'cell', + "featureType": 'gene', + "sampleType": 'sample', + }, + ).add_file( + file_type='anndata.zarr', + url=base_url, + coordination_values={ + "obsType": 'cell', + "featureType": 'gene', + "featureValueType": 'expression', + "sampleType": 'sample', + }, + options={ + "obsFeatureMatrix": { + "path": 'layers/pearson_residuals', + }, + "obsEmbedding": [ + { + "path": 'obsm/X_densmap', + "embeddingType": 'densMAP', + }, + ], + "obsSets": [ + { + "name": 'Cell Type', + "path": 'obs/cell_type', + }, + { + "name": 'Subclass L1', + "path": 'obs/subclass_l1', + }, + { + "name": 'Subclass L2', + "path": 'obs/subclass_l2', + }, + { + "name": 'Donor ID', + "path": 'obs/donor_id', + }, + ], + "sampleEdges": { + "path": 'obs/SampleID', + }, + }, + ).add_file( + file_type='sampleSets.anndata.zarr', + url=f"{base_url}/uns/__all__.samples", + options={ + "sampleSets": [ + { + "name": 'Disease Type', + "path": 'diseasetype', + }, + { + "name": 'Adjudicated Category', + "path": 'AdjudicatedCategory', + }, + { + "name": 'Enrollment Category', + "path": 'EnrollmentCategory', + }, + ], + }, + coordination_values= { + "sampleType": 'sample', + }, + ) + + biomarkerSelect = vc.add_view('biomarkerSelect', dataset=dataset, uid='biomarker-select') + comparativeHeading = vc.add_view('comparativeHeading', dataset=dataset, uid='comparative-heading') + dualScatterplot = vc.add_view('dualScatterplot', dataset=dataset, uid='scatterplot') + obsSets = vc.add_view('obsSets', dataset=dataset, uid='cell-sets') + sampleSets = vc.add_view('sampleSetPairManager', dataset=dataset, uid='sample-sets') + obsSetSizes = vc.add_view('obsSetSizes', dataset=dataset) + featureList = vc.add_view('featureList', dataset=dataset) + violinPlots = vc.add_view('obsSetFeatureValueDistribution', dataset=dataset, uid='violin-plot') + dotPlot = vc.add_view('dotPlot', dataset=dataset, uid='dot-plot') + treemap = vc.add_view('treemap', dataset=dataset, uid='treemap') + volcanoPlot = vc.add_view('volcanoPlot', dataset=dataset, uid='volcano-plot') + volcanoPlotTable = vc.add_view('featureStatsTable', dataset=dataset, uid='volcano-plot-table') + obsSetCompositionBarPlot = vc.add_view('obsSetCompositionBarPlot', dataset=dataset, uid='sccoda-plot') + featureSetEnrichmentBarPlot = vc.add_view('featureSetEnrichmentBarPlot', dataset=dataset, uid='pathways-plot') + + [sampleSetScope_caseControl] = vc.add_coordination('sampleSetSelection') + sampleSetScope_caseControl.set_value([['Disease Type', 'CKD'], ['Disease Type', 'Reference']]) + + [featureSelectionScope] = vc.add_coordination('featureSelection') + featureSelectionScope.set_value(['UMOD', 'NPHS2']) + + vc.link_views_by_dict([dualScatterplot], { + "embeddingType": 'densMAP', + "embeddingContoursVisible": True, + "embeddingPointsVisible": False, + "embeddingObsSetLabelsVisible": True, + }, meta=False); + + + vc.link_views([biomarkerSelect, dualScatterplot, obsSets, obsSetSizes, featureList, violinPlots, dotPlot, treemap, volcanoPlot, volcanoPlotTable, comparativeHeading, obsSetCompositionBarPlot, featureSetEnrichmentBarPlot, sampleSets], ['sampleType'], ['sample']) + + vc.link_views_by_dict([biomarkerSelect, dualScatterplot, obsSets, obsSetSizes, featureList, violinPlots, dotPlot, treemap, volcanoPlot, volcanoPlotTable, comparativeHeading, obsSetCompositionBarPlot, featureSetEnrichmentBarPlot, sampleSets], { + "sampleSetSelection": sampleSetScope_caseControl, + "featureSelection": featureSelectionScope, + }, meta=False) + + vc.link_views_by_dict([dualScatterplot, violinPlots, featureList, dotPlot], { + # "featureSelection": ['UMOD', 'NPHS2'], // , 'ENSG00000074803', 'ENSG00000164825'], + "obsColorEncoding": 'geneSelection', + "featureValueColormap": 'jet', + "featureValueColormapRange": [0, 0.25], + "featureAggregationStrategy": None, + }, meta=False) + + vc.layout(hconcat( + vconcat(dualScatterplot, biomarkerSelect, comparativeHeading, obsSets, obsSetSizes, featureList), + vconcat(treemap, featureSetEnrichmentBarPlot, violinPlots, dotPlot, obsSetCompositionBarPlot, sampleSets), + volcanoPlotTable, + )); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Define the page layout + """ + ) + return + + +@app.cell +def _(transform): + PAGE_ESM = transform(""" + import clsx from "https://unpkg.com/clsx@1.1.1/dist/clsx.m.js"; + + function createPage(utilsForPages) { + const { + React, + usePageModeView, + } = utilsForPages; + function PageComponent(props) { + const BiomarkerSelect = usePageModeView('biomarker-select'); + const ComparativeHeading = usePageModeView('comparative-heading'); + const CellSets = usePageModeView('cell-sets'); + const SampleSets = usePageModeView('sample-sets'); + const DualScatterplot = usePageModeView('scatterplot'); + const ViolinPlot = usePageModeView('violin-plot'); + const DotPlot = usePageModeView('dot-plot'); + const Treemap = usePageModeView('treemap'); + const VolcanoPlot = usePageModeView('volcano-plot'); + const VolcanoPlotTable = usePageModeView('volcano-plot-table'); + const SccodaPlot = usePageModeView('sccoda-plot'); + const PathwaysPlot = usePageModeView('pathways-plot'); + + + return ( + <> + +
+
+

Comparative visualization of single-cell atlas data

+ +
+
+ +
+
+
+ +
+
+
+

This view contains a treemap visualization to communicate cell type composition in each of the selected sample groups.

+
+
+ +
+
+
+
+

This view displays the results of a cell type composition analysis performed using the ScCODA algorithm (Büttner et al. 2021). Cell types with significantly different composition between the selected sample groups are displayed opaque while not-signficant results are displayed with transparent bars. The single outlined bar denotes the automatically-selected reference cell type.

+
+
+ +
+
+
+
+

This view displays differential expression test results, performed using the rank_genes_groups function from Scanpy (Wolf et al. 2018) with method "wilcoxon".

The arrows on the bottom left and bottom right denote the direction of the effect. Click a point in the plot to select the corresponding gene.

Note that differential expression tests have been run for each cell type separately, so the each gene can appear multiple times (once per cell type). If there are too many points on the plot, cell types can be selected to filter the points.

+
+
+ +
+
+
+
+

This view displays differential expression test results in tabular form. Click a row in the table to select the corresponding gene.

+
+
+ +
+
+
+
+

This view displays gene set enrichment test results based on the differential expression results. Gene set enrichment tests have been performed using Reactome 2022 pathway gene sets from BlitzGSEA (Lachmann et al. 2022) via the hypergeometric function of Pertpy (Heumos et al. 2024).

+
+
+ +
+
+
+
+

This view contains contour scatterplots which display the results of a density-preserving dimensionality reduction (Narayan et al. 2021). Contour opacities correspond to the shown percentile thresholds.

+
+
+ +
+
+
+
+

This dot plot view displays gene expression values per cell type and sample group for the selected biomarkers.

+
+
+ +
+
+
+
+

This violin plot view displays gene expression values per cell type and sample group for the selected biomarker.

+
+
+ +
+
+ {/*

Neighborhood-level representations

+

TODO

+

Segmented instance-level representations

+

TODO

+

Image-level representations

+

TODO

+

Participant-level representations

+

TODO

*/} +
+
+
+ +
+
+ +
+
+ +
+ + ); + } + return PageComponent; + } + export default { createPage }; + """) + return (PAGE_ESM,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Render page as widget + """ + ) + return + + +@app.cell +def _(PAGE_ESM, vc): + vw = vc.widget(page_esm=PAGE_ESM, page_mode=True, height=4700, prevent_scroll=False) + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/page_mode_example.ipynb b/docs/notebooks/page_mode_example.ipynb deleted file mode 100644 index 031111c8..00000000 --- a/docs/notebooks/page_mode_example.ipynb +++ /dev/null @@ -1,197 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Page mode example" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - " CsvWrapper,\n", - ")\n", - "from oxc_py import transform" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure the data and views" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "url = 'https://storage.googleapis.com/vitessce-demo-data/anndata-test/pbmc3k_processed.zarr'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.17\", name='PBMC Reference')\n", - "dataset = vc.add_dataset(name='PBMC 3k').add_object(\n", - " AnnDataWrapper(\n", - " adata_url=url,\n", - " obs_set_paths=[\"obs/louvain\"],\n", - " obs_set_names=[\"Louvain\"],\n", - " obs_embedding_paths=[\"obsm/X_umap\", \"obsm/X_pca\"],\n", - " obs_embedding_names=[\"UMAP\", \"PCA\"],\n", - " obs_feature_matrix_path=\"X\"\n", - " )\n", - ")\n", - "\n", - "umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\", uid=\"scatterplot-umap\")\n", - "pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"PCA\", uid=\"scatterplot-pca\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset, uid=\"cell-sets\")\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset, uid=\"gene-list\")\n", - "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset, uid=\"heatmap\")\n", - "\n", - "vc.layout((umap / pca) | ((cell_sets | genes) / heatmap));" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## Define the page layout" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "PAGE_ESM = transform(\"\"\"\n", - "function createPage(utilsForPages) {\n", - " const {\n", - " React,\n", - " usePageModeView,\n", - " } = utilsForPages;\n", - " function PageComponent(props) {\n", - " const ScatterplotUmap = usePageModeView('scatterplot-umap');\n", - " const ScatterplotPca = usePageModeView('scatterplot-pca');\n", - " const CellSets = usePageModeView('cell-sets');\n", - " const GeneList = usePageModeView('gene-list');\n", - " const Heatmap = usePageModeView('heatmap');\n", - " \n", - " return (\n", - " <>\n", - " \n", - "
\n", - "
\n", - "

This is an arbitrary HTML element with custom CSS

\n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - " \n", - "
\n", - "
\n", - "

Another HTML element

\n", - "
\n", - " \n", - "
\n", - "
\n", - "
\n", - " \n", - " \n", - "
\n", - "
\n", - "\n", - " \n", - " );\n", - " }\n", - " return PageComponent;\n", - "}\n", - "export default { createPage };\n", - "\"\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Render page as widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget(page_esm=PAGE_ESM, page_mode=True, height=1100)\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/page_mode_example.mo.py b/docs/notebooks/page_mode_example.mo.py new file mode 100644 index 00000000..1ff38f0f --- /dev/null +++ b/docs/notebooks/page_mode_example.mo.py @@ -0,0 +1,175 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Page mode example + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + CsvWrapper, + ) + from oxc_py import transform + return AnnDataWrapper, VitessceConfig, cm, transform + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure the data and views + """ + ) + return + + +@app.cell +def _(): + url = 'https://storage.googleapis.com/vitessce-demo-data/anndata-test/pbmc3k_processed.zarr' + return (url,) + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, cm, url): + vc = VitessceConfig(schema_version="1.0.17", name='PBMC Reference') + dataset = vc.add_dataset(name='PBMC 3k').add_object( + AnnDataWrapper( + adata_url=url, + obs_set_paths=["obs/louvain"], + obs_set_names=["Louvain"], + obs_embedding_paths=["obsm/X_umap", "obsm/X_pca"], + obs_embedding_names=["UMAP", "PCA"], + obs_feature_matrix_path="X" + ) + ) + + umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP", uid="scatterplot-umap") + pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="PCA", uid="scatterplot-pca") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset, uid="cell-sets") + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset, uid="gene-list") + heatmap = vc.add_view(cm.HEATMAP, dataset=dataset, uid="heatmap") + + vc.layout((umap / pca) | ((cell_sets | genes) / heatmap)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Define the page layout + """ + ) + return + + +@app.cell +def _(transform): + PAGE_ESM = transform(""" + function createPage(utilsForPages) { + const { + React, + usePageModeView, + } = utilsForPages; + function PageComponent(props) { + const ScatterplotUmap = usePageModeView('scatterplot-umap'); + const ScatterplotPca = usePageModeView('scatterplot-pca'); + const CellSets = usePageModeView('cell-sets'); + const GeneList = usePageModeView('gene-list'); + const Heatmap = usePageModeView('heatmap'); + + return ( + <> + +
+
+

This is an arbitrary HTML element with custom CSS

+
+
+ +
+
+ +
+
+

Another HTML element

+
+ +
+
+
+ + +
+
+ + + ); + } + return PageComponent; + } + export default { createPage }; + """) + return (PAGE_ESM,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Render page as widget + """ + ) + return + + +@app.cell +def _(PAGE_ESM, vc): + vw = vc.widget(page_esm=PAGE_ESM, page_mode=True, height=1100) + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/spatial_data.ipynb b/docs/notebooks/spatial_data.ipynb deleted file mode 100644 index 1a6eb831..00000000 --- a/docs/notebooks/spatial_data.ipynb +++ /dev/null @@ -1,164 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of a SpatialData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Import dependencies\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "import zipfile\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - " SpatialDataWrapper,\n", - " get_initial_coordination_scope_prefix\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "data_dir = \"data\"\n", - "zip_filepath = join(data_dir, \"visium.spatialdata.zarr.zip\")\n", - "spatialdata_filepath = join(data_dir, \"visium.spatialdata.zarr\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "if not isdir(spatialdata_filepath):\n", - " if not isfile(zip_filepath):\n", - " os.makedirs(data_dir, exist_ok=True)\n", - " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/visium_associated_xenium_io.zip', zip_filepath)\n", - " with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n", - " zip_ref.extractall(data_dir)\n", - " os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(\n", - " schema_version=\"1.0.18\",\n", - " name='Visium SpatialData Demo (visium_associated_xenium_io)',\n", - ")\n", - "# Add data to the configuration:\n", - "wrapper = SpatialDataWrapper(\n", - " sdata_path=spatialdata_filepath,\n", - " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", - " image_path=\"images/CytAssist_FFPE_Human_Breast_Cancer_full_image\",\n", - " table_path=\"tables/table\",\n", - " obs_feature_matrix_path=\"tables/table/X\",\n", - " obs_spots_path=\"shapes/CytAssist_FFPE_Human_Breast_Cancer\",\n", - " region=\"CytAssist_FFPE_Human_Breast_Cancer\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " # The following tells Vitessce to consider each observation as a \"spot\"\n", - " \"obsType\": \"spot\",\n", - " }\n", - ")\n", - "dataset = vc.add_dataset(name='Breast Cancer Visium').add_object(wrapper)\n", - "\n", - "# Add views (visualizations) to the configuration:\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "feature_list = vc.add_view(vt.FEATURE_LIST, dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'imageLayer': CL([{\n", - " 'photometricInterpretation': 'RGB',\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", - "vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], [wrapper.obs_type_label])\n", - "\n", - "# Layout the views\n", - "vc.layout(spatial | (feature_list / layer_controller / obs_sets));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Render the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/spatial_data.mo.py b/docs/notebooks/spatial_data.mo.py new file mode 100644 index 00000000..413791ec --- /dev/null +++ b/docs/notebooks/spatial_data.mo.py @@ -0,0 +1,171 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of a SpatialData object + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Import dependencies + + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + import zipfile + + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + SpatialDataWrapper, + get_initial_coordination_scope_prefix + ) + return ( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + isdir, + isfile, + join, + os, + urlretrieve, + vt, + zipfile, + ) + + +@app.cell +def _(join): + data_dir = "data" + zip_filepath = join(data_dir, "visium.spatialdata.zarr.zip") + spatialdata_filepath = join(data_dir, "visium.spatialdata.zarr") + return data_dir, spatialdata_filepath, zip_filepath + + +@app.cell +def _( + data_dir, + isdir, + isfile, + join, + os, + spatialdata_filepath, + urlretrieve, + zip_filepath, + zipfile, +): + if not isdir(spatialdata_filepath): + if not isfile(zip_filepath): + os.makedirs(data_dir, exist_ok=True) + urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/visium_associated_xenium_io.zip', zip_filepath) + with zipfile.ZipFile(zip_filepath,"r") as zip_ref: + zip_ref.extractall(data_dir) + os.rename(join(data_dir, "data.zarr"), spatialdata_filepath) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + """ + ) + return + + +@app.cell +def _( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + spatialdata_filepath, + vt, +): + vc = VitessceConfig( + schema_version="1.0.18", + name='Visium SpatialData Demo (visium_associated_xenium_io)', + ) + # Add data to the configuration: + wrapper = SpatialDataWrapper( + sdata_path=spatialdata_filepath, + # The following paths are relative to the root of the SpatialData zarr store on-disk. + image_path="images/CytAssist_FFPE_Human_Breast_Cancer_full_image", + table_path="tables/table", + obs_feature_matrix_path="tables/table/X", + obs_spots_path="shapes/CytAssist_FFPE_Human_Breast_Cancer", + region="CytAssist_FFPE_Human_Breast_Cancer", + coordinate_system="global", + coordination_values={ + # The following tells Vitessce to consider each observation as a "spot" + "obsType": "spot", + } + ) + dataset = vc.add_dataset(name='Breast Cancer Visium').add_object(wrapper) + + # Add views (visualizations) to the configuration: + spatial = vc.add_view("spatialBeta", dataset=dataset) + feature_list = vc.add_view(vt.FEATURE_LIST, dataset=dataset) + layer_controller = vc.add_view("layerControllerBeta", dataset=dataset) + vc.link_views_by_dict([spatial, layer_controller], { + 'imageLayer': CL([{ + 'photometricInterpretation': 'RGB', + }]), + }, scope_prefix=get_initial_coordination_scope_prefix("A", "image")) + obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset) + vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], [wrapper.obs_type_label]) + + # Layout the views + vc.layout(spatial | (feature_list / layer_controller / obs_sets)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### Render the widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/spatial_data_blobs.ipynb b/docs/notebooks/spatial_data_blobs.ipynb deleted file mode 100644 index 717e9645..00000000 --- a/docs/notebooks/spatial_data_blobs.ipynb +++ /dev/null @@ -1,201 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of a SpatialData object, blobs example" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Import dependencies\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import spatialdata\n", - "from os.path import join" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sdata = spatialdata.datasets.blobs()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "spatialdata_filepath = join(\"data\", \"blobs.spatialdata.zarr\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sdata.write(spatialdata_filepath, overwrite=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - " SpatialDataWrapper,\n", - " get_initial_coordination_scope_prefix\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(\n", - " schema_version=\"1.0.18\",\n", - " name='Visium SpatialData Demo (blobs)',\n", - ")\n", - "# Add data to the configuration:\n", - "wrapper = SpatialDataWrapper(\n", - " sdata_store=spatialdata_filepath,\n", - " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", - " image_path=\"images/blobs_image\",\n", - " obs_segmentations_path=\"labels/blobs_labels\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"obsType\": \"blob\",\n", - " \"fileUid\": \"my_unique_id\"\n", - " }\n", - ")\n", - "dataset = vc.add_dataset(name='Blobs').add_object(wrapper)\n", - "\n", - "# Add views (visualizations) to the configuration:\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'imageLayer': CL([{\n", - " \"fileUid\": \"my_unique_id\",\n", - " 'photometricInterpretation': 'BlackIsZero',\n", - " 'spatialLayerOpacity': 0.9,\n", - " 'imageChannel': CL([\n", - " {\n", - " \"spatialTargetC\": 0,\n", - " \"spatialChannelColor\": [255, 0, 0],\n", - " \"spatialChannelOpacity\": 1.0\n", - " },\n", - " {\n", - " \"spatialTargetC\": 1,\n", - " \"spatialChannelColor\": [0, 255, 0],\n", - " \"spatialChannelOpacity\": 1.0\n", - " },\n", - " {\n", - " \"spatialTargetC\": 2,\n", - " \"spatialChannelColor\": [0, 0, 255],\n", - " \"spatialChannelOpacity\": 1.0\n", - " }\n", - " ])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'segmentationLayer': CL([{\n", - " \"fileUid\": \"my_unique_id\",\n", - " 'segmentationChannel': CL([{\n", - " 'spatialChannelVisible': True,\n", - " 'obsType': 'blob',\n", - " }]),\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", - "\n", - "# Layout the views\n", - "vc.layout(spatial | layer_controller);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Render the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/spatial_data_blobs.mo.py b/docs/notebooks/spatial_data_blobs.mo.py new file mode 100644 index 00000000..cc8d23ee --- /dev/null +++ b/docs/notebooks/spatial_data_blobs.mo.py @@ -0,0 +1,177 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of a SpatialData object, blobs example + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Import dependencies + + """ + ) + return + + +@app.cell +def _(): + import spatialdata + from os.path import join + return join, spatialdata + + +@app.cell +def _(spatialdata): + sdata = spatialdata.datasets.blobs() + return (sdata,) + + +@app.cell +def _(join): + spatialdata_filepath = join("data", "blobs.spatialdata.zarr") + return (spatialdata_filepath,) + + +@app.cell +def _(sdata, spatialdata_filepath): + sdata.write(spatialdata_filepath, overwrite=True) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + SpatialDataWrapper, + get_initial_coordination_scope_prefix + ) + return ( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + """ + ) + return + + +@app.cell +def _( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + spatialdata_filepath, +): + vc = VitessceConfig( + schema_version="1.0.18", + name='Visium SpatialData Demo (blobs)', + ) + # Add data to the configuration: + wrapper = SpatialDataWrapper( + sdata_store=spatialdata_filepath, + # The following paths are relative to the root of the SpatialData zarr store on-disk. + image_path="images/blobs_image", + obs_segmentations_path="labels/blobs_labels", + coordinate_system="global", + coordination_values={ + "obsType": "blob", + "fileUid": "my_unique_id" + } + ) + dataset = vc.add_dataset(name='Blobs').add_object(wrapper) + + # Add views (visualizations) to the configuration: + spatial = vc.add_view("spatialBeta", dataset=dataset) + layer_controller = vc.add_view("layerControllerBeta", dataset=dataset) + + vc.link_views_by_dict([spatial, layer_controller], { + 'imageLayer': CL([{ + "fileUid": "my_unique_id", + 'photometricInterpretation': 'BlackIsZero', + 'spatialLayerOpacity': 0.9, + 'imageChannel': CL([ + { + "spatialTargetC": 0, + "spatialChannelColor": [255, 0, 0], + "spatialChannelOpacity": 1.0 + }, + { + "spatialTargetC": 1, + "spatialChannelColor": [0, 255, 0], + "spatialChannelOpacity": 1.0 + }, + { + "spatialTargetC": 2, + "spatialChannelColor": [0, 0, 255], + "spatialChannelOpacity": 1.0 + } + ]) + }]), + }, scope_prefix=get_initial_coordination_scope_prefix("A", "image")) + + vc.link_views_by_dict([spatial, layer_controller], { + 'segmentationLayer': CL([{ + "fileUid": "my_unique_id", + 'segmentationChannel': CL([{ + 'spatialChannelVisible': True, + 'obsType': 'blob', + }]), + }]), + }, scope_prefix=get_initial_coordination_scope_prefix("A", "obsSegmentations")) + + # Layout the views + vc.layout(spatial | layer_controller); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### Render the widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/spatial_data_kpmp_vis.ipynb b/docs/notebooks/spatial_data_kpmp_vis.ipynb deleted file mode 100644 index e10e51c5..00000000 --- a/docs/notebooks/spatial_data_kpmp_vis.ipynb +++ /dev/null @@ -1,477 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Import dependencies\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Reference: https://github.com/vitessce/vitessce/blob/main/examples/configs/src/view-configs/spatial-beta/kpmp.js" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - " SpatialDataWrapper,\n", - " AnnDataWrapper,\n", - " ImageOmeTiffWrapper,\n", - " ImageOmeZarrWrapper,\n", - " ObsSegmentationsOmeZarrWrapper,\n", - " get_initial_coordination_scope_prefix,\n", - " hconcat,\n", - " vconcat,\n", - ")\n", - "from os.path import join" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sdata_url = \"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/sdata.zarr\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create a VitessceConfig instance.\n", - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"SpatialData\")\n", - "\n", - "t_obstype = \"Tubule\"\n", - "a_obstype = \"Artery\"\n", - "ci_obstype = \"Cortical Interstitium\"\n", - "gsg_obstype = \"G. S. Glomerulus\"\n", - "ngsg_obstype = \"Non-G. S. Glomerulus\"\n", - "ifta_obstype = \"Interstitial Fibrosis and Tubular Atrophy\"\n", - "ptc_obstype = \"Peritubular Capillaries\"\n", - "\n", - "# Add a new dataset to the Vitessce configuration,\n", - "# then add the wrapper class instance to this dataset.\n", - "dataset = vc.add_dataset(name='KPMP').add_object(\n", - " SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " image_path=\"images/image\",\n", - " coordinate_system=\"global\",\n", - " )\n", - ").add_object(\n", - " SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " table_path=\"tables/table_tubules\",\n", - " obs_segmentations_path=\"labels/labels_tubules\",\n", - " obs_feature_matrix_path=\"tables/table_tubules/X\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"fileUid\": \"labels_tubules\",\n", - " \"obsType\": t_obstype,\n", - " \"featureType\": 'feature',\n", - " \"featureValueType\": 'value',\n", - " }\n", - " )\n", - ").add_object(\n", - " SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " obs_segmentations_path=\"labels/labels_arteries_arterioles\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"fileUid\": \"labels_arteries_arterioles\",\n", - " \"obsType\": a_obstype,\n", - " \"featureType\": 'feature',\n", - " \"featureValueType\": 'value',\n", - " }\n", - " )\n", - ").add_object(\n", - " SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " table_path=\"tables/table_cortical_interstitia\",\n", - " obs_segmentations_path=\"labels/labels_cortical_interstitia\",\n", - " obs_feature_matrix_path=\"tables/table_cortical_interstitia/X\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"fileUid\": \"labels_cortical_interstitia\",\n", - " \"obsType\": ci_obstype,\n", - " \"featureType\": 'feature',\n", - " \"featureValueType\": 'value',\n", - " }\n", - " )\n", - ").add_object(\n", - " SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " table_path=\"tables/table_globally_sclerotic_glomeruli\",\n", - " obs_segmentations_path=\"labels/labels_globally_sclerotic_glomeruli\",\n", - " obs_feature_matrix_path=\"tables/table_globally_sclerotic_glomeruli/X\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"fileUid\": \"labels_globally_sclerotic_glomeruli\",\n", - " \"obsType\": gsg_obstype,\n", - " \"featureType\": 'feature',\n", - " \"featureValueType\": 'value',\n", - " }\n", - " )\n", - ").add_object(\n", - " SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " table_path=\"tables/table_non_globally_sclerotic_glomeruli\",\n", - " obs_segmentations_path=\"labels/labels_non_globally_sclerotic_glomeruli\",\n", - " obs_feature_matrix_path=\"tables/table_non_globally_sclerotic_glomeruli/X\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"fileUid\": \"labels_non_globally_sclerotic_glomeruli\",\n", - " \"obsType\": ngsg_obstype,\n", - " \"featureType\": 'feature',\n", - " \"featureValueType\": 'value',\n", - " }\n", - " )\n", - ").add_object(\n", - " SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " table_path=\"tables/table_interstitialfibrosis_and_tubular_atrophy\",\n", - " obs_segmentations_path=\"labels/labels_interstitialfibrosis_and_tubular_atrophy\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"fileUid\": \"labels_interstitialfibrosis_and_tubular_atrophy\",\n", - " \"obsType\": ifta_obstype,\n", - " \"featureType\": 'feature',\n", - " \"featureValueType\": 'value',\n", - " }\n", - " )\n", - ").add_object(\n", - " SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " table_path=\"tables/table_peritubular_capillaries\",\n", - " obs_segmentations_path=\"labels/labels_peritubular_capillaries\",\n", - " obs_feature_matrix_path=\"tables/table_peritubular_capillaries/X\",\n", - " obs_set_paths=[\n", - " \"tables/table_peritubular_capillaries/obs/cortex_ifta_set\",\n", - " \"tables/table_peritubular_capillaries/obs/cortex_set\",\n", - " \"tables/table_peritubular_capillaries/obs/ifta_set\",\n", - " [\"tables/table_peritubular_capillaries/obs/cortex_set\", \"tables/table_peritubular_capillaries/obs/ifta_set\"],\n", - " ],\n", - " obs_set_names=[\n", - " \"Cortex and IFTA membership\",\n", - " \"Cortex membership\",\n", - " \"IFTA membership\",\n", - " \"Cortex and IFTA hierarchy\",\n", - " ],\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"fileUid\": \"labels_peritubular_capillaries\",\n", - " \"obsType\": ptc_obstype,\n", - " \"featureType\": 'feature',\n", - " \"featureValueType\": 'value',\n", - " }\n", - " )\n", - ")\n", - "\n", - "# Add views (visualizations) to the configuration.\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "# Tubules\n", - "tubules_feature_list = vc.add_view(\"featureList\", dataset=dataset).set_props(title=\"Tubules\")\n", - "tubules_histogram = vc.add_view(\"featureValueHistogram\", dataset=dataset)\n", - "# Peritubular capillaries\n", - "pt_feature_list = vc.add_view(\"featureList\", dataset=dataset).set_props(title=\"Peritubular Capillaries\")\n", - "pt_histogram = vc.add_view(\"featureValueHistogram\", dataset=dataset)\n", - "# GSG\n", - "gsg_feature_list = vc.add_view(\"featureList\", dataset=dataset).set_props(title=\"Globally Sclerotic Glomeruli\")\n", - "gsg_histogram = vc.add_view(\"featureValueHistogram\", dataset=dataset)\n", - "# NGSG\n", - "ngsg_feature_list = vc.add_view(\"featureList\", dataset=dataset).set_props(title=\"Non-Globally Sclerotic Glomeruli\")\n", - "ngsg_histogram = vc.add_view(\"featureValueHistogram\", dataset=dataset)\n", - "\n", - "# Add obsSets, obsSetSizes, and violin plot views for PTC sets+areas/aspectRatio\n", - "pt_sets = vc.add_view(\"obsSets\", dataset=dataset)\n", - "pt_bar_plot = vc.add_view(\"obsSetSizes\", dataset=dataset)\n", - "pt_violin_plot = vc.add_view(\"obsSetFeatureValueDistribution\", dataset=dataset).set_props(jitter=True)\n", - "\n", - "\n", - "# Coordination of views.\n", - "[ft_scope, fvt_scope] = vc.add_coordination(\"featureType\", \"featureValueType\")\n", - "ft_scope.set_value(\"feature\")\n", - "fvt_scope.set_value(\"value\")\n", - "\n", - "[t_ot_scope, t_fs_scope, t_oce_scope] = vc.add_coordination(\"obsType\", \"featureSelection\", \"obsColorEncoding\")\n", - "t_ot_scope.set_value(t_obstype)\n", - "t_oce_scope.set_value(\"spatialChannelColor\")\n", - "\n", - "[pt_ot_scope, pt_fs_scope, pt_oce_scope] = vc.add_coordination(\"obsType\", \"featureSelection\", \"obsColorEncoding\")\n", - "pt_ot_scope.set_value(ptc_obstype)\n", - "pt_fs_scope.set_value([\"Area\"])\n", - "pt_oce_scope.set_value(\"cellSetSelection\")\n", - "\n", - "\n", - "[gsg_ot_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope] = vc.add_coordination(\"obsType\", \"featureSelection\", \"obsColorEncoding\", \"featureValueColormapRange\")\n", - "gsg_ot_scope.set_value(gsg_obstype)\n", - "gsg_oce_scope.set_value(\"spatialChannelColor\")\n", - "gsg_fvcr_scope.set_value([(3077 - 2333) / (29911 - 2333), 1.0])\n", - "\n", - "[ngsg_ot_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope] = vc.add_coordination(\"obsType\", \"featureSelection\", \"obsColorEncoding\", \"featureValueColormapRange\")\n", - "ngsg_ot_scope.set_value(ngsg_obstype)\n", - "ngsg_oce_scope.set_value(\"spatialChannelColor\")\n", - "ngsg_fvcr_scope.set_value([0.0, 1 - (59451 - 29911) / (59451 - 3077)])\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " \"imageLayer\": CL([{\n", - " \"spatialLayerOpacity\": 0.1,\n", - " \"photometricInterpretation\": \"RGB\",\n", - " }]),\n", - "}, meta=True, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " \"segmentationLayer\": CL([\n", - " {\n", - " \"fileUid\": \"labels_tubules\",\n", - " \"segmentationChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"obsType\": t_ot_scope,\n", - " \"featureType\": ft_scope,\n", - " \"featureValueType\": fvt_scope,\n", - " \"featureSelection\": t_fs_scope,\n", - " \"spatialChannelVisible\": False,\n", - " \"spatialChannelColor\": [73, 155, 119],\n", - " \"spatialChannelOpacity\": 0.5,\n", - " \"obsColorEncoding\": t_oce_scope,\n", - " \"featureValueColormapRange\": [0, 1],\n", - " \"featureAggregationStrategy\": \"first\",\n", - " \"spatialSegmentationFilled\": True,\n", - " \"obsHighlight\": None,\n", - " }]),\n", - " },\n", - " {\n", - " \"fileUid\": \"labels_arteries_arterioles\",\n", - " \"segmentationChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"obsType\": \"Artery\",\n", - " \"featureType\": ft_scope,\n", - " \"featureValueType\": fvt_scope,\n", - " \"spatialChannelVisible\": False,\n", - " \"spatialChannelColor\": [237, 226, 107],\n", - " \"spatialChannelOpacity\": 0.5,\n", - " \"obsColorEncoding\": \"spatialChannelColor\",\n", - " \"featureValueColormapRange\": [0, 1],\n", - " \"featureAggregationStrategy\": \"first\",\n", - " \"spatialSegmentationFilled\": True,\n", - " \"obsHighlight\": None,\n", - " }]),\n", - " },\n", - " {\n", - " \"fileUid\": \"labels_cortical_interstitia\",\n", - " \"segmentationChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"obsType\": \"Cortical Interstitium\",\n", - " \"featureType\": ft_scope,\n", - " \"featureValueType\": fvt_scope,\n", - " \"spatialChannelVisible\": False,\n", - " \"spatialChannelColor\": [255, 255, 255],\n", - " \"spatialChannelOpacity\": 0.5,\n", - " \"obsColorEncoding\": \"spatialChannelColor\",\n", - " \"featureValueColormapRange\": [0, 1],\n", - " \"featureAggregationStrategy\": \"first\",\n", - " \"spatialSegmentationFilled\": True,\n", - " \"obsHighlight\": None,\n", - " }]),\n", - " },\n", - " {\n", - " \"fileUid\": \"labels_globally_sclerotic_glomeruli\",\n", - " \"segmentationChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"obsType\": gsg_ot_scope,\n", - " \"featureType\": ft_scope,\n", - " \"featureValueType\": fvt_scope,\n", - " \"featureSelection\": gsg_fs_scope,\n", - " \"spatialChannelVisible\": False,\n", - " \"spatialChannelColor\": [52, 113, 171],\n", - " \"spatialChannelOpacity\": 0.5,\n", - " \"obsColorEncoding\": gsg_oce_scope,\n", - " \"featureValueColormapRange\": gsg_fvcr_scope,\n", - " \"featureAggregationStrategy\": \"first\",\n", - " \"spatialSegmentationFilled\": True,\n", - " \"obsHighlight\": None,\n", - " }]),\n", - " },\n", - " {\n", - " \"fileUid\": \"labels_non_globally_sclerotic_glomeruli\",\n", - " \"segmentationChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"obsType\": ngsg_ot_scope,\n", - " \"featureType\": ft_scope,\n", - " \"featureValueType\": fvt_scope,\n", - " \"featureSelection\": ngsg_fs_scope,\n", - " \"spatialChannelVisible\": False,\n", - " \"spatialChannelColor\": [114, 179, 226],\n", - " \"spatialChannelOpacity\": 0.5,\n", - " \"obsColorEncoding\": ngsg_oce_scope,\n", - " \"featureValueColormapRange\": ngsg_fvcr_scope,\n", - " \"featureAggregationStrategy\": \"first\",\n", - " \"spatialSegmentationFilled\": True,\n", - " \"obsHighlight\": None,\n", - " }]),\n", - " },\n", - " {\n", - " \"fileUid\": \"labels_interstitialfibrosis_and_tubular_atrophy\",\n", - " \"segmentationChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"obsType\": \"Interstitial Fibrosis and Tubular Atrophy\",\n", - " \"featureType\": ft_scope,\n", - " \"featureValueType\": fvt_scope,\n", - " \"spatialChannelVisible\": True,\n", - " \"spatialChannelColor\": [218, 161, 66],\n", - " \"spatialChannelOpacity\": 1.0,\n", - " \"obsColorEncoding\": \"spatialChannelColor\",\n", - " \"featureValueColormapRange\": [0, 1],\n", - " \"featureAggregationStrategy\": \"first\",\n", - " \"spatialSegmentationFilled\": False,\n", - " \"obsHighlight\": None,\n", - " }]),\n", - " },\n", - " {\n", - " \"fileUid\": \"labels_peritubular_capillaries\",\n", - " \"segmentationChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"obsType\": pt_ot_scope,\n", - " \"featureType\": ft_scope,\n", - " \"featureValueType\": fvt_scope,\n", - " \"featureSelection\": pt_fs_scope,\n", - " \"spatialChannelVisible\": True,\n", - " \"spatialChannelColor\": [197, 101, 47],\n", - " \"spatialChannelOpacity\": 1.0,\n", - " \"obsColorEncoding\": pt_oce_scope,\n", - " \"featureValueColormapRange\": [0, 0.5],\n", - " \"featureAggregationStrategy\": \"first\",\n", - " \"spatialSegmentationFilled\": True,\n", - " \"obsHighlight\": None,\n", - " }]),\n", - " }\n", - " ]),\n", - "}, meta=True, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", - "\n", - "tubules_feature_list.use_coordination(t_ot_scope, ft_scope, fvt_scope, t_fs_scope, t_oce_scope)\n", - "tubules_histogram.use_coordination(t_ot_scope, ft_scope, fvt_scope, t_fs_scope, t_oce_scope)\n", - "\n", - "pt_feature_list.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", - "pt_histogram.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", - "\n", - "pt_sets.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", - "pt_bar_plot.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", - "pt_violin_plot.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope)\n", - "\n", - "\n", - "gsg_feature_list.use_coordination(gsg_ot_scope, ft_scope, fvt_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope)\n", - "gsg_histogram.use_coordination(gsg_ot_scope, ft_scope, fvt_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope)\n", - "\n", - "ngsg_feature_list.use_coordination(ngsg_ot_scope, ft_scope, fvt_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope)\n", - "ngsg_histogram.use_coordination(ngsg_ot_scope, ft_scope, fvt_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope)\n", - "\n", - "\n", - "# Layout the views in a grid arrangement.\n", - "vc.layout(vconcat(\n", - " hconcat(spatial, layer_controller, split=[3, 1]),\n", - " hconcat(\n", - " (tubules_feature_list / tubules_histogram),\n", - " (pt_feature_list / pt_histogram),\n", - " (gsg_feature_list / gsg_histogram),\n", - " (ngsg_feature_list / ngsg_histogram)\n", - " ),\n", - " hconcat(pt_sets, pt_bar_plot, pt_violin_plot)\n", - "));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget(height=1000)\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#import json\n", - "#print(json.dumps(vc.to_dict(), indent=2))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/spatial_data_kpmp_vis.mo.py b/docs/notebooks/spatial_data_kpmp_vis.mo.py new file mode 100644 index 00000000..43db087f --- /dev/null +++ b/docs/notebooks/spatial_data_kpmp_vis.mo.py @@ -0,0 +1,450 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of a SpatialData object + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Import dependencies + + """ + ) + return + + +@app.cell +def _(): + # Reference: https://github.com/vitessce/vitessce/blob/main/examples/configs/src/view-configs/spatial-beta/kpmp.js + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + SpatialDataWrapper, + AnnDataWrapper, + ImageOmeTiffWrapper, + ImageOmeZarrWrapper, + ObsSegmentationsOmeZarrWrapper, + get_initial_coordination_scope_prefix, + hconcat, + vconcat, + ) + from os.path import join + return ( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + hconcat, + vconcat, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + """ + ) + return + + +@app.cell +def _(): + sdata_url = "https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/sdata.zarr" + return (sdata_url,) + + +@app.cell +def _( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + hconcat, + sdata_url, + vconcat, +): + # Create a VitessceConfig instance. + vc = VitessceConfig(schema_version="1.0.18", name="SpatialData") + + t_obstype = "Tubule" + a_obstype = "Artery" + ci_obstype = "Cortical Interstitium" + gsg_obstype = "G. S. Glomerulus" + ngsg_obstype = "Non-G. S. Glomerulus" + ifta_obstype = "Interstitial Fibrosis and Tubular Atrophy" + ptc_obstype = "Peritubular Capillaries" + + # Add a new dataset to the Vitessce configuration, + # then add the wrapper class instance to this dataset. + dataset = vc.add_dataset(name='KPMP').add_object( + SpatialDataWrapper( + sdata_url=sdata_url, + image_path="images/image", + coordinate_system="global", + ) + ).add_object( + SpatialDataWrapper( + sdata_url=sdata_url, + table_path="tables/table_tubules", + obs_segmentations_path="labels/labels_tubules", + obs_feature_matrix_path="tables/table_tubules/X", + coordinate_system="global", + coordination_values={ + "fileUid": "labels_tubules", + "obsType": t_obstype, + "featureType": 'feature', + "featureValueType": 'value', + } + ) + ).add_object( + SpatialDataWrapper( + sdata_url=sdata_url, + obs_segmentations_path="labels/labels_arteries_arterioles", + coordinate_system="global", + coordination_values={ + "fileUid": "labels_arteries_arterioles", + "obsType": a_obstype, + "featureType": 'feature', + "featureValueType": 'value', + } + ) + ).add_object( + SpatialDataWrapper( + sdata_url=sdata_url, + table_path="tables/table_cortical_interstitia", + obs_segmentations_path="labels/labels_cortical_interstitia", + obs_feature_matrix_path="tables/table_cortical_interstitia/X", + coordinate_system="global", + coordination_values={ + "fileUid": "labels_cortical_interstitia", + "obsType": ci_obstype, + "featureType": 'feature', + "featureValueType": 'value', + } + ) + ).add_object( + SpatialDataWrapper( + sdata_url=sdata_url, + table_path="tables/table_globally_sclerotic_glomeruli", + obs_segmentations_path="labels/labels_globally_sclerotic_glomeruli", + obs_feature_matrix_path="tables/table_globally_sclerotic_glomeruli/X", + coordinate_system="global", + coordination_values={ + "fileUid": "labels_globally_sclerotic_glomeruli", + "obsType": gsg_obstype, + "featureType": 'feature', + "featureValueType": 'value', + } + ) + ).add_object( + SpatialDataWrapper( + sdata_url=sdata_url, + table_path="tables/table_non_globally_sclerotic_glomeruli", + obs_segmentations_path="labels/labels_non_globally_sclerotic_glomeruli", + obs_feature_matrix_path="tables/table_non_globally_sclerotic_glomeruli/X", + coordinate_system="global", + coordination_values={ + "fileUid": "labels_non_globally_sclerotic_glomeruli", + "obsType": ngsg_obstype, + "featureType": 'feature', + "featureValueType": 'value', + } + ) + ).add_object( + SpatialDataWrapper( + sdata_url=sdata_url, + table_path="tables/table_interstitialfibrosis_and_tubular_atrophy", + obs_segmentations_path="labels/labels_interstitialfibrosis_and_tubular_atrophy", + coordinate_system="global", + coordination_values={ + "fileUid": "labels_interstitialfibrosis_and_tubular_atrophy", + "obsType": ifta_obstype, + "featureType": 'feature', + "featureValueType": 'value', + } + ) + ).add_object( + SpatialDataWrapper( + sdata_url=sdata_url, + table_path="tables/table_peritubular_capillaries", + obs_segmentations_path="labels/labels_peritubular_capillaries", + obs_feature_matrix_path="tables/table_peritubular_capillaries/X", + obs_set_paths=[ + "tables/table_peritubular_capillaries/obs/cortex_ifta_set", + "tables/table_peritubular_capillaries/obs/cortex_set", + "tables/table_peritubular_capillaries/obs/ifta_set", + ["tables/table_peritubular_capillaries/obs/cortex_set", "tables/table_peritubular_capillaries/obs/ifta_set"], + ], + obs_set_names=[ + "Cortex and IFTA membership", + "Cortex membership", + "IFTA membership", + "Cortex and IFTA hierarchy", + ], + coordinate_system="global", + coordination_values={ + "fileUid": "labels_peritubular_capillaries", + "obsType": ptc_obstype, + "featureType": 'feature', + "featureValueType": 'value', + } + ) + ) + + # Add views (visualizations) to the configuration. + spatial = vc.add_view("spatialBeta", dataset=dataset) + layer_controller = vc.add_view("layerControllerBeta", dataset=dataset) + # Tubules + tubules_feature_list = vc.add_view("featureList", dataset=dataset).set_props(title="Tubules") + tubules_histogram = vc.add_view("featureValueHistogram", dataset=dataset) + # Peritubular capillaries + pt_feature_list = vc.add_view("featureList", dataset=dataset).set_props(title="Peritubular Capillaries") + pt_histogram = vc.add_view("featureValueHistogram", dataset=dataset) + # GSG + gsg_feature_list = vc.add_view("featureList", dataset=dataset).set_props(title="Globally Sclerotic Glomeruli") + gsg_histogram = vc.add_view("featureValueHistogram", dataset=dataset) + # NGSG + ngsg_feature_list = vc.add_view("featureList", dataset=dataset).set_props(title="Non-Globally Sclerotic Glomeruli") + ngsg_histogram = vc.add_view("featureValueHistogram", dataset=dataset) + + # Add obsSets, obsSetSizes, and violin plot views for PTC sets+areas/aspectRatio + pt_sets = vc.add_view("obsSets", dataset=dataset) + pt_bar_plot = vc.add_view("obsSetSizes", dataset=dataset) + pt_violin_plot = vc.add_view("obsSetFeatureValueDistribution", dataset=dataset).set_props(jitter=True) + + + # Coordination of views. + [ft_scope, fvt_scope] = vc.add_coordination("featureType", "featureValueType") + ft_scope.set_value("feature") + fvt_scope.set_value("value") + + [t_ot_scope, t_fs_scope, t_oce_scope] = vc.add_coordination("obsType", "featureSelection", "obsColorEncoding") + t_ot_scope.set_value(t_obstype) + t_oce_scope.set_value("spatialChannelColor") + + [pt_ot_scope, pt_fs_scope, pt_oce_scope] = vc.add_coordination("obsType", "featureSelection", "obsColorEncoding") + pt_ot_scope.set_value(ptc_obstype) + pt_fs_scope.set_value(["Area"]) + pt_oce_scope.set_value("cellSetSelection") + + + [gsg_ot_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope] = vc.add_coordination("obsType", "featureSelection", "obsColorEncoding", "featureValueColormapRange") + gsg_ot_scope.set_value(gsg_obstype) + gsg_oce_scope.set_value("spatialChannelColor") + gsg_fvcr_scope.set_value([(3077 - 2333) / (29911 - 2333), 1.0]) + + [ngsg_ot_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope] = vc.add_coordination("obsType", "featureSelection", "obsColorEncoding", "featureValueColormapRange") + ngsg_ot_scope.set_value(ngsg_obstype) + ngsg_oce_scope.set_value("spatialChannelColor") + ngsg_fvcr_scope.set_value([0.0, 1 - (59451 - 29911) / (59451 - 3077)]) + + vc.link_views_by_dict([spatial, layer_controller], { + "imageLayer": CL([{ + "spatialLayerOpacity": 0.1, + "photometricInterpretation": "RGB", + }]), + }, meta=True, scope_prefix=get_initial_coordination_scope_prefix("A", "image")) + + vc.link_views_by_dict([spatial, layer_controller], { + "segmentationLayer": CL([ + { + "fileUid": "labels_tubules", + "segmentationChannel": CL([{ + "spatialTargetC": 0, + "obsType": t_ot_scope, + "featureType": ft_scope, + "featureValueType": fvt_scope, + "featureSelection": t_fs_scope, + "spatialChannelVisible": False, + "spatialChannelColor": [73, 155, 119], + "spatialChannelOpacity": 0.5, + "obsColorEncoding": t_oce_scope, + "featureValueColormapRange": [0, 1], + "featureAggregationStrategy": "first", + "spatialSegmentationFilled": True, + "obsHighlight": None, + }]), + }, + { + "fileUid": "labels_arteries_arterioles", + "segmentationChannel": CL([{ + "spatialTargetC": 0, + "obsType": "Artery", + "featureType": ft_scope, + "featureValueType": fvt_scope, + "spatialChannelVisible": False, + "spatialChannelColor": [237, 226, 107], + "spatialChannelOpacity": 0.5, + "obsColorEncoding": "spatialChannelColor", + "featureValueColormapRange": [0, 1], + "featureAggregationStrategy": "first", + "spatialSegmentationFilled": True, + "obsHighlight": None, + }]), + }, + { + "fileUid": "labels_cortical_interstitia", + "segmentationChannel": CL([{ + "spatialTargetC": 0, + "obsType": "Cortical Interstitium", + "featureType": ft_scope, + "featureValueType": fvt_scope, + "spatialChannelVisible": False, + "spatialChannelColor": [255, 255, 255], + "spatialChannelOpacity": 0.5, + "obsColorEncoding": "spatialChannelColor", + "featureValueColormapRange": [0, 1], + "featureAggregationStrategy": "first", + "spatialSegmentationFilled": True, + "obsHighlight": None, + }]), + }, + { + "fileUid": "labels_globally_sclerotic_glomeruli", + "segmentationChannel": CL([{ + "spatialTargetC": 0, + "obsType": gsg_ot_scope, + "featureType": ft_scope, + "featureValueType": fvt_scope, + "featureSelection": gsg_fs_scope, + "spatialChannelVisible": False, + "spatialChannelColor": [52, 113, 171], + "spatialChannelOpacity": 0.5, + "obsColorEncoding": gsg_oce_scope, + "featureValueColormapRange": gsg_fvcr_scope, + "featureAggregationStrategy": "first", + "spatialSegmentationFilled": True, + "obsHighlight": None, + }]), + }, + { + "fileUid": "labels_non_globally_sclerotic_glomeruli", + "segmentationChannel": CL([{ + "spatialTargetC": 0, + "obsType": ngsg_ot_scope, + "featureType": ft_scope, + "featureValueType": fvt_scope, + "featureSelection": ngsg_fs_scope, + "spatialChannelVisible": False, + "spatialChannelColor": [114, 179, 226], + "spatialChannelOpacity": 0.5, + "obsColorEncoding": ngsg_oce_scope, + "featureValueColormapRange": ngsg_fvcr_scope, + "featureAggregationStrategy": "first", + "spatialSegmentationFilled": True, + "obsHighlight": None, + }]), + }, + { + "fileUid": "labels_interstitialfibrosis_and_tubular_atrophy", + "segmentationChannel": CL([{ + "spatialTargetC": 0, + "obsType": "Interstitial Fibrosis and Tubular Atrophy", + "featureType": ft_scope, + "featureValueType": fvt_scope, + "spatialChannelVisible": True, + "spatialChannelColor": [218, 161, 66], + "spatialChannelOpacity": 1.0, + "obsColorEncoding": "spatialChannelColor", + "featureValueColormapRange": [0, 1], + "featureAggregationStrategy": "first", + "spatialSegmentationFilled": False, + "obsHighlight": None, + }]), + }, + { + "fileUid": "labels_peritubular_capillaries", + "segmentationChannel": CL([{ + "spatialTargetC": 0, + "obsType": pt_ot_scope, + "featureType": ft_scope, + "featureValueType": fvt_scope, + "featureSelection": pt_fs_scope, + "spatialChannelVisible": True, + "spatialChannelColor": [197, 101, 47], + "spatialChannelOpacity": 1.0, + "obsColorEncoding": pt_oce_scope, + "featureValueColormapRange": [0, 0.5], + "featureAggregationStrategy": "first", + "spatialSegmentationFilled": True, + "obsHighlight": None, + }]), + } + ]), + }, meta=True, scope_prefix=get_initial_coordination_scope_prefix("A", "obsSegmentations")) + + tubules_feature_list.use_coordination(t_ot_scope, ft_scope, fvt_scope, t_fs_scope, t_oce_scope) + tubules_histogram.use_coordination(t_ot_scope, ft_scope, fvt_scope, t_fs_scope, t_oce_scope) + + pt_feature_list.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope) + pt_histogram.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope) + + pt_sets.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope) + pt_bar_plot.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope) + pt_violin_plot.use_coordination(pt_ot_scope, ft_scope, fvt_scope, pt_fs_scope, pt_oce_scope) + + + gsg_feature_list.use_coordination(gsg_ot_scope, ft_scope, fvt_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope) + gsg_histogram.use_coordination(gsg_ot_scope, ft_scope, fvt_scope, gsg_fs_scope, gsg_oce_scope, gsg_fvcr_scope) + + ngsg_feature_list.use_coordination(ngsg_ot_scope, ft_scope, fvt_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope) + ngsg_histogram.use_coordination(ngsg_ot_scope, ft_scope, fvt_scope, ngsg_fs_scope, ngsg_oce_scope, ngsg_fvcr_scope) + + + # Layout the views in a grid arrangement. + vc.layout(vconcat( + hconcat(spatial, layer_controller, split=[3, 1]), + hconcat( + (tubules_feature_list / tubules_histogram), + (pt_feature_list / pt_histogram), + (gsg_feature_list / gsg_histogram), + (ngsg_feature_list / ngsg_histogram) + ), + hconcat(pt_sets, pt_bar_plot, pt_violin_plot) + )); + return (vc,) + + +@app.cell +def _(vc): + vw = vc.widget(height=1000) + vw + return + + +@app.cell +def _(): + #import json + #print(json.dumps(vc.to_dict(), indent=2)) + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/spatial_data_merfish.ipynb b/docs/notebooks/spatial_data_merfish.ipynb deleted file mode 100644 index 11e8e9e0..00000000 --- a/docs/notebooks/spatial_data_merfish.ipynb +++ /dev/null @@ -1,208 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of a SpatialData object, blobs example" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Import dependencies\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sdata_url = \"https://data-2.vitessce.io/data/moffitt/merfish_mouse_ileum.sdata.zarr\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - " SpatialDataWrapper,\n", - " get_initial_coordination_scope_prefix\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(\n", - " schema_version=\"1.0.18\",\n", - " name='SpatialData with MERFISH data',\n", - ")\n", - "# Add data to the configuration:\n", - "\n", - "dataset = vc.add_dataset(name='MERFISH').add_object(SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", - " image_path=\"images/stains\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"fileUid\": \"stains\"\n", - " }\n", - ")).add_object(SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", - " obs_segmentations_path=\"labels/dapi_labels\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"obsType\": \"nucleus\",\n", - " \"fileUid\": \"dapi\"\n", - " }\n", - ")).add_object(SpatialDataWrapper(\n", - " sdata_url=sdata_url,\n", - " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", - " obs_segmentations_path=\"labels/membrane_labels\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"obsType\": \"cell\",\n", - " \"fileUid\": \"membrane\"\n", - " }\n", - "))\n", - "\n", - "# Add views (visualizations) to the configuration:\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'imageLayer': CL([{\n", - " \"fileUid\": \"stains\",\n", - " 'photometricInterpretation': 'BlackIsZero',\n", - " 'spatialLayerOpacity': 1.0,\n", - " 'spatialLayerVisible': True,\n", - " 'imageChannel': CL([\n", - " {\n", - " 'spatialChannelVisible': True,\n", - " \"spatialTargetC\": 0, # DAPI, Nucleus\n", - " \"spatialChannelColor\": [0, 0, 255],\n", - " \"spatialChannelOpacity\": 1.0\n", - " },\n", - " {\n", - " 'spatialChannelVisible': True,\n", - " \"spatialTargetC\": 1, # Membrane, Cell\n", - " \"spatialChannelColor\": [255, 255, 255],\n", - " \"spatialChannelOpacity\": 1.0\n", - " }\n", - " ])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'segmentationLayer': CL([{\n", - " \"fileUid\": \"membrane\",\n", - " 'spatialLayerOpacity': 1.0,\n", - " 'spatialLayerVisible': True,\n", - " 'segmentationChannel': CL([{\n", - " 'spatialChannelVisible': True,\n", - " 'obsType': 'cell',\n", - " \"spatialChannelColor\": [200, 200, 200],\n", - " \"obsColorEncoding\": \"spatialChannelColor\",\n", - " }]),\n", - " }, {\n", - " \"fileUid\": \"dapi\",\n", - " 'spatialLayerOpacity': 1.0,\n", - " 'spatialLayerVisible': True,\n", - " 'segmentationChannel': CL([{\n", - " 'spatialChannelVisible': True,\n", - " 'obsType': 'nucleus',\n", - " \"spatialChannelColor\": [255, 255, 255],\n", - " \"obsColorEncoding\": \"spatialChannelColor\",\n", - " }]),\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", - "\n", - "# Layout the views\n", - "vc.layout(spatial | layer_controller);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Render the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/spatial_data_merfish.mo.py b/docs/notebooks/spatial_data_merfish.mo.py new file mode 100644 index 00000000..42645033 --- /dev/null +++ b/docs/notebooks/spatial_data_merfish.mo.py @@ -0,0 +1,186 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of a SpatialData object, blobs example + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Import dependencies + + """ + ) + return + + +@app.cell +def _(): + sdata_url = "https://data-2.vitessce.io/data/moffitt/merfish_mouse_ileum.sdata.zarr" + return (sdata_url,) + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + SpatialDataWrapper, + get_initial_coordination_scope_prefix + ) + return ( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + """ + ) + return + + +@app.cell +def _( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + sdata_url, +): + vc = VitessceConfig( + schema_version="1.0.18", + name='SpatialData with MERFISH data', + ) + # Add data to the configuration: + + dataset = vc.add_dataset(name='MERFISH').add_object(SpatialDataWrapper( + sdata_url=sdata_url, + # The following paths are relative to the root of the SpatialData zarr store on-disk. + image_path="images/stains", + coordinate_system="global", + coordination_values={ + "fileUid": "stains" + } + )).add_object(SpatialDataWrapper( + sdata_url=sdata_url, + # The following paths are relative to the root of the SpatialData zarr store on-disk. + obs_segmentations_path="labels/dapi_labels", + coordinate_system="global", + coordination_values={ + "obsType": "nucleus", + "fileUid": "dapi" + } + )).add_object(SpatialDataWrapper( + sdata_url=sdata_url, + # The following paths are relative to the root of the SpatialData zarr store on-disk. + obs_segmentations_path="labels/membrane_labels", + coordinate_system="global", + coordination_values={ + "obsType": "cell", + "fileUid": "membrane" + } + )) + + # Add views (visualizations) to the configuration: + spatial = vc.add_view("spatialBeta", dataset=dataset) + layer_controller = vc.add_view("layerControllerBeta", dataset=dataset) + + vc.link_views_by_dict([spatial, layer_controller], { + 'imageLayer': CL([{ + "fileUid": "stains", + 'photometricInterpretation': 'BlackIsZero', + 'spatialLayerOpacity': 1.0, + 'spatialLayerVisible': True, + 'imageChannel': CL([ + { + 'spatialChannelVisible': True, + "spatialTargetC": 0, # DAPI, Nucleus + "spatialChannelColor": [0, 0, 255], + "spatialChannelOpacity": 1.0 + }, + { + 'spatialChannelVisible': True, + "spatialTargetC": 1, # Membrane, Cell + "spatialChannelColor": [255, 255, 255], + "spatialChannelOpacity": 1.0 + } + ]) + }]), + }, scope_prefix=get_initial_coordination_scope_prefix("A", "image")) + + vc.link_views_by_dict([spatial, layer_controller], { + 'segmentationLayer': CL([{ + "fileUid": "membrane", + 'spatialLayerOpacity': 1.0, + 'spatialLayerVisible': True, + 'segmentationChannel': CL([{ + 'spatialChannelVisible': True, + 'obsType': 'cell', + "spatialChannelColor": [200, 200, 200], + "obsColorEncoding": "spatialChannelColor", + }]), + }, { + "fileUid": "dapi", + 'spatialLayerOpacity': 1.0, + 'spatialLayerVisible': True, + 'segmentationChannel': CL([{ + 'spatialChannelVisible': True, + 'obsType': 'nucleus', + "spatialChannelColor": [255, 255, 255], + "obsColorEncoding": "spatialChannelColor", + }]), + }]), + }, scope_prefix=get_initial_coordination_scope_prefix("A", "obsSegmentations")) + + # Layout the views + vc.layout(spatial | layer_controller); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### Render the widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/spatial_data_merfish_2.ipynb b/docs/notebooks/spatial_data_merfish_2.ipynb deleted file mode 100644 index 74296a55..00000000 --- a/docs/notebooks/spatial_data_merfish_2.ipynb +++ /dev/null @@ -1,172 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of a SpatialData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Import dependencies\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "import zipfile\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - " SpatialDataWrapper,\n", - " get_initial_coordination_scope_prefix\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "data_dir = \"data\"\n", - "zip_filepath = join(data_dir, \"merfish_2.spatialdata.zarr.zip\")\n", - "spatialdata_filepath = join(data_dir, \"merfish_2.spatialdata.zarr\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "if not isdir(spatialdata_filepath):\n", - " if not isfile(zip_filepath):\n", - " os.makedirs(data_dir, exist_ok=True)\n", - " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/merfish.zip', zip_filepath)\n", - " with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n", - " zip_ref.extractall(data_dir)\n", - " os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(\n", - " schema_version=\"1.0.18\",\n", - " name='MERFISH SpatialData Demo',\n", - ")\n", - "# Add data to the configuration:\n", - "wrapper = SpatialDataWrapper(\n", - " sdata_path=spatialdata_filepath,\n", - " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", - " image_path=\"images/rasterized\",\n", - " table_path=\"tables/table\",\n", - " obs_feature_matrix_path=\"tables/table/X\",\n", - " obs_spots_path=\"shapes/cells\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " # The following tells Vitessce to consider each observation as a \"spot\"\n", - " \"obsType\": \"cell\",\n", - " }\n", - ")\n", - "dataset = vc.add_dataset(name='MERFISH').add_object(wrapper)\n", - "\n", - "# Add views (visualizations) to the configuration:\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "feature_list = vc.add_view(\"featureList\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "obs_sets = vc.add_view(\"obsSets\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'spotLayer': CL([{\n", - " 'obsType': 'cell',\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSpots\"))\n", - "\n", - "vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], [wrapper.obs_type_label])\n", - "\n", - "# Layout the views\n", - "vc.layout(spatial | (feature_list / layer_controller / obs_sets));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Render the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/spatial_data_merfish_2.mo.py b/docs/notebooks/spatial_data_merfish_2.mo.py new file mode 100644 index 00000000..76b0160f --- /dev/null +++ b/docs/notebooks/spatial_data_merfish_2.mo.py @@ -0,0 +1,170 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of a SpatialData object + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Import dependencies + + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + import zipfile + + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + SpatialDataWrapper, + get_initial_coordination_scope_prefix + ) + return ( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + isdir, + isfile, + join, + os, + urlretrieve, + zipfile, + ) + + +@app.cell +def _(join): + data_dir = "data" + zip_filepath = join(data_dir, "merfish_2.spatialdata.zarr.zip") + spatialdata_filepath = join(data_dir, "merfish_2.spatialdata.zarr") + return data_dir, spatialdata_filepath, zip_filepath + + +@app.cell +def _( + data_dir, + isdir, + isfile, + join, + os, + spatialdata_filepath, + urlretrieve, + zip_filepath, + zipfile, +): + if not isdir(spatialdata_filepath): + if not isfile(zip_filepath): + os.makedirs(data_dir, exist_ok=True) + urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/merfish.zip', zip_filepath) + with zipfile.ZipFile(zip_filepath,"r") as zip_ref: + zip_ref.extractall(data_dir) + os.rename(join(data_dir, "data.zarr"), spatialdata_filepath) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + """ + ) + return + + +@app.cell +def _( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + spatialdata_filepath, +): + vc = VitessceConfig( + schema_version="1.0.18", + name='MERFISH SpatialData Demo', + ) + # Add data to the configuration: + wrapper = SpatialDataWrapper( + sdata_path=spatialdata_filepath, + # The following paths are relative to the root of the SpatialData zarr store on-disk. + image_path="images/rasterized", + table_path="tables/table", + obs_feature_matrix_path="tables/table/X", + obs_spots_path="shapes/cells", + coordinate_system="global", + coordination_values={ + # The following tells Vitessce to consider each observation as a "spot" + "obsType": "cell", + } + ) + dataset = vc.add_dataset(name='MERFISH').add_object(wrapper) + + # Add views (visualizations) to the configuration: + spatial = vc.add_view("spatialBeta", dataset=dataset) + feature_list = vc.add_view("featureList", dataset=dataset) + layer_controller = vc.add_view("layerControllerBeta", dataset=dataset) + obs_sets = vc.add_view("obsSets", dataset=dataset) + + vc.link_views_by_dict([spatial, layer_controller], { + 'spotLayer': CL([{ + 'obsType': 'cell', + }]), + }, scope_prefix=get_initial_coordination_scope_prefix("A", "obsSpots")) + + vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], [wrapper.obs_type_label]) + + # Layout the views + vc.layout(spatial | (feature_list / layer_controller / obs_sets)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### Render the widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/spatial_data_mouseliver.ipynb b/docs/notebooks/spatial_data_mouseliver.ipynb deleted file mode 100644 index e1078c49..00000000 --- a/docs/notebooks/spatial_data_mouseliver.ipynb +++ /dev/null @@ -1,789 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "# Visualization of a SpatialData object and individual Spatial Elements, local data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook explains how to create interactive visualizations of data that is accessible locally.\n", - "\n", - "\n", - "We progress through different visualization tasks, first demonstrating how Vitessce facilitates integrated imaging and spatial single-cell visualizations, then demonstrating visualization of non-spatial and image-only datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Utility dependencies" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, we import utility dependencies which will be used to download the example dataset and manipulate file paths, zip files, and JSON files." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "import zipfile\n", - "import json" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dependencies for Vitessce" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we import classes and functions from the `vitessce` Python package.\n", - "This package includes not only APIs for [visualization configuration](https://python-docs.vitessce.io/api_config.html) but also [helper functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) for basic data transformation tasks.\n", - "To specify mappings between data fields and visualization properties, the package contains [classes](https://python-docs.vitessce.io/api_data.html#module-vitessce.wrappers) which wrap standard single-cell data structures stored in formats including [AnnData](https://doi.org/10.1101/2021.12.16.473007), [SpatialData](https://doi.org/10.1038/s41592-024-02212-x), [OME-TIFF](https://doi.org/10.1007/978-3-030-23937-4_1), and [OME-Zarr](https://doi.org/10.1038/s41592-021-01326-w):\n", - "\n", - "- [AnnDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.AnnDataWrapper)\n", - "- [ImageOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeTiffWrapper)\n", - "- [ImageOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeZarrWrapper)\n", - "- [ObsSegmentationsOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeTiffWrapper)\n", - "- [ObsSegmentationsOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeZarrWrapper)\n", - "- [SpatialDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.SpatialDataWrapper)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - " SpatialDataWrapper,\n", - " AnnDataWrapper,\n", - " ImageOmeTiffWrapper,\n", - " ImageOmeZarrWrapper,\n", - " ObsSegmentationsOmeZarrWrapper,\n", - " get_initial_coordination_scope_prefix,\n", - " hconcat,\n", - " vconcat,\n", - ")\n", - "from vitessce.data_utils import (\n", - " VAR_CHUNK_SIZE,\n", - " generate_h5ad_ref_spec,\n", - " multiplex_img_to_ome_tiff,\n", - " multiplex_img_to_ome_zarr,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dependencies for data structures" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this blog post, we perform basic data transformation tasks to save individual elements of an integrated SpatialData object to separate files in AnnData, OME-TIFF, and OME-Zarr formats.\n", - "To perform these data transformations, we import the following dependencies.\n", - "In general, you will typically not need to import all of these dependencies, either because you are only working with data in one of these formats, or because the data you intend to visualize is already saved to a file or directory.\n", - "\n", - "Note: Dependencies such as `spatialdata` may need to be installed before they can be imported in the next code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "from spatialdata import read_zarr\n", - "from anndata import AnnData\n", - "from ome_zarr.writer import write_image\n", - "import tifffile\n", - "from generate_tiff_offsets import get_offsets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Download example dataset" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We download a mouse liver dataset which serves as a SpatialData [example dataset](https://github.com/scverse/spatialdata-notebooks/blob/main/notebooks/examples/transformations.ipynb).\n", - "\n", - "This dataset was generated by [Guilliams et al.](https://doi.org/10.1016/j.cell.2021.12.018) and processed using [SPArrOW](https://doi.org/10.1101/2024.07.04.601829) during the SpatialData [developer workshop](https://doi.org/10.37044/osf.io/8ck3e) in 2024." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "data_dir = \"data\"\n", - "zip_filepath = join(data_dir, \"mouse_liver.spatialdata.zarr.zip\")\n", - "spatialdata_filepath = join(data_dir, \"mouse_liver.spatialdata.zarr\")\n", - "adata_zarr_filepath = join(data_dir, \"mouse_liver.anndata.zarr\")\n", - "adata_h5ad_filepath = join(data_dir, \"mouse_liver.h5ad\")\n", - "ref_spec_json_filepath = join(data_dir, \"mouse_liver.h5ad.ref.json\")\n", - "ome_tiff_filepath = join(data_dir, \"mouse_liver.ome.tif\")\n", - "offsets_json_filepath = join(data_dir, \"mouse_liver.ome.tif.offsets.json\")\n", - "ome_zarr_filepath = join(data_dir, \"mouse_liver.ome.zarr\")\n", - "labels_ome_zarr_filepath = join(data_dir, \"mouse_liver.labels.ome.zarr\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following code uses Python's `urlretrieve` to download the SpatialData object as a zip file, then unzips the file using the `zipfile` module." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "if not isdir(spatialdata_filepath):\n", - " if not isfile(zip_filepath):\n", - " os.makedirs(data_dir, exist_ok=True)\n", - " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/mouse_liver.zip', zip_filepath)\n", - " with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n", - " zip_ref.extractall(data_dir)\n", - " os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## Visualization of a SpatialData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "SpatialData objects are the most complex type of data structure we will work with in this blog post.\n", - "SpatialData objects function as contains for multiple types of Spatial Elements:\n", - "\n", - "- Tables (each table is represented as an AnnData object)\n", - "- Points (e.g., coordinates of transcripts from FISH-based experiments)\n", - "- Shapes (vector-based shapes such as polygons and circles)\n", - "- Labels (label images, i.e., segmentation bitmasks; each label image is stored using OME-Zarr)\n", - "- Images (microscopy images; each image is stored using OME-Zarr)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views.\n", - "To visualize data stored in a SpatialData object, we use the `SpatialDataWrapper` class and specify the paths (relative to the root of the Zarr [directory store](https://zarr.readthedocs.io/en/v2.18.5/api/storage.html#zarr.storage.DirectoryStore)) to different spatial elements of interest." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create a VitessceConfig instance.\n", - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"SpatialData Demo\")\n", - "\n", - "# Instantiate the wrapper class, specifying data fields of interest.\n", - "wrapper = SpatialDataWrapper(\n", - " sdata_path=spatialdata_filepath,\n", - " # The following paths are relative to the root of the SpatialData Zarr store on-disk.\n", - " table_path=\"tables/table\",\n", - " image_path=\"images/raw_image\",\n", - " obs_segmentations_path=\"labels/segmentation_mask\",\n", - " obs_feature_matrix_path=\"tables/table/X\",\n", - " obs_set_paths=[\"tables/table/obs/annotation\"],\n", - " obs_set_names=[\"Annotation\"],\n", - " region=\"nucleus_boundaries\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"obsType\": \"cell\" \n", - " }\n", - ")\n", - "# Add a new dataset to the Vitessce configuration,\n", - "# then add the wrapper class instance to this dataset.\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(wrapper)\n", - "\n", - "# Add views (visualizations) to the configuration.\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "feature_list = vc.add_view(\"featureList\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "obs_sets = vc.add_view(\"obsSets\", dataset=dataset)\n", - "heatmap = vc.add_view(\"heatmap\", dataset=dataset)\n", - "\n", - "[obs_color_encoding_scope] = vc.add_coordination(\"obsColorEncoding\")\n", - "obs_color_encoding_scope.set_value(\"cellSetSelection\")\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " \"imageLayer\": CL([{\n", - " \"photometricInterpretation\": \"BlackIsZero\",\n", - " \"imageChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"spatialChannelColor\": [255, 255, 255],\n", - " \"spatialChannelWindow\": [0, 4000],\n", - " }])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " \"segmentationLayer\": CL([{\n", - " \"segmentationChannel\": CL([{\n", - " \"obsColorEncoding\": obs_color_encoding_scope,\n", - " }]),\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", - "\n", - "vc.link_views([spatial, layer_controller, feature_list, obs_sets, heatmap], [\"obsType\"], [wrapper.obs_type_label])\n", - "vc.link_views_by_dict([feature_list, obs_sets, heatmap], {\n", - " \"obsColorEncoding\": obs_color_encoding_scope,\n", - "}, meta=False)\n", - "\n", - "# Layout the views in a grid arrangement.\n", - "vc.layout((spatial / heatmap) | (layer_controller / (feature_list | obs_sets)));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Render the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Extract AnnData object from SpatialData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The above example demonstrates how to visualize a spatial 'omics dataset containing not only single-cell information (e.g., a cell-by-gene expression matrix, cell type annotations) but also an image and cell segmentations.\n", - "To demonstrate how to use Vitessce to visualize data from a (non-spatial) single-cell experiment, we will extract this information from the SpatialData object and save it to a simpler [AnnData](https://anndata.readthedocs.io/) object (ignoring the imaging and spatially-resolved elements)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sdata = read_zarr(spatialdata_filepath)\n", - "sdata" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = sdata.tables['table']\n", - "adata" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As Zarr-formatted data can be easily visualized by Vitessce, we recommend saving the AnnData object to a Zarr store using the [write_zarr](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_zarr.html) method.\n", - "Optionally, the shape of array chunks (for the AnnData `X` array) can be specified as a parameter, to optimize performance based on data access patterns.\n", - "For example, a common pattern is to visualize data across all cells for one gene.\n", - "To support such a pattern, the chunk shape can be specified as follows, `(total number of cells, small number of genes)`, resulting in tall-and-skinny array chunks." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata.write_zarr(adata_zarr_filepath, chunks=(adata.shape[0], VAR_CHUNK_SIZE))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternatively, your AnnData object may already be stored using the H5AD (HDF5-based) format.\n", - "To demonstrate this scenario, we save the object using the [write_h5ad](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_h5ad.html) method." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata.write_h5ad(adata_h5ad_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To read H5AD-formatted data, Vitessce requires an accompanying JSON [references specification](https://fsspec.github.io/kerchunk/spec.html) file, which can be constructed using the `generate_h5ad_ref_spec` utility function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ref_dict = generate_h5ad_ref_spec(adata_h5ad_filepath)\n", - "with open(ref_spec_json_filepath, \"w\") as f:\n", - " json.dump(ref_dict, f)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualization of an AnnData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Zarr-based AnnData" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"AnnData (zarr)\")\n", - "# Add data.\n", - "wrapper = AnnDataWrapper(\n", - " adata_path=adata_zarr_filepath,\n", - " obs_feature_matrix_path=\"X\",\n", - " obs_set_paths=[\"obs/annotation\"],\n", - " obs_set_names=[\"Annotation\"],\n", - " coordination_values={\n", - " \"obsType\": \"cell\" \n", - " }\n", - ")\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(wrapper)\n", - "\n", - "# Add views.\n", - "heatmap = vc.add_view(vt.HEATMAP, dataset=dataset)\n", - "feature_list = vc.add_view(vt.FEATURE_LIST, dataset=dataset)\n", - "obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", - "violin_plots = vc.add_view(\"obsSetFeatureValueDistribution\", dataset=dataset)\n", - "\n", - "vc.link_views([heatmap, feature_list, obs_sets], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]])\n", - "\n", - "# Layout the views.\n", - "vc.layout((heatmap / violin_plots) | (feature_list / obs_sets));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.widget()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### H5AD-based AnnData" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"AnnData (h5ad)\")\n", - "# Add data.\n", - "wrapper = AnnDataWrapper(\n", - " adata_path=adata_h5ad_filepath,\n", - " ref_path=ref_spec_json_filepath,\n", - " obs_feature_matrix_path=\"X\",\n", - " obs_set_paths=[\"obs/annotation\"],\n", - " obs_set_names=[\"Annotation\"],\n", - " coordination_values={\n", - " \"obsType\": \"cell\" \n", - " }\n", - ")\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(wrapper)\n", - "\n", - "# Add views.\n", - "heatmap = vc.add_view(vt.HEATMAP, dataset=dataset)\n", - "feature_list = vc.add_view(vt.FEATURE_LIST, dataset=dataset)\n", - "obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", - "violin_plots = vc.add_view(\"obsSetFeatureValueDistribution\", dataset=dataset)\n", - "\n", - "vc.link_views([heatmap, feature_list, obs_sets], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]])\n", - "\n", - "# Layout the views.\n", - "vc.layout((heatmap / violin_plots) | (feature_list / obs_sets));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.widget()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Extract image from SpatialData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In contrast to extraction of the non-spatial data from the SpatialData object, we can extract the imaging (and segmentation/label image) data and save it to a dedicated bioimaging file format." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "img_arr = sdata.images['raw_image'].to_numpy()\n", - "labels_arr = sdata.labels['segmentation_mask'].to_numpy()\n", - "labels_arr = labels_arr[np.newaxis, :]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Save image to OME-Zarr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For small images, the data can be saved to OME-Zarr or OME-TIFF format using [utility functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) from the Vitessce package.\n", - "Larger images require generation of an [image pyramid](https://en.wikipedia.org/wiki/Pyramid_(image_processing)), which can be performed using tools from the OME ecosystem such as [bioformats2raw](https://github.com/glencoesoftware/bioformats2raw) and [raw2ometiff](https://github.com/glencoesoftware/raw2ometiff)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "multiplex_img_to_ome_zarr(img_arr, [\"Channel 0\"], ome_zarr_filepath)\n", - "multiplex_img_to_ome_zarr(labels_arr, [\"cell\"], labels_ome_zarr_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Save image and segmentations to OME-TIFF" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To efficiently visualize OME-TIFF data using Vitessce, a JSON-based offsets file can be constructed using [generate-tiff-offsets](https://github.com/hms-dbmi/generate-tiff-offsets).\n", - "This JSON file contains byte offsets into different partitions of the TIFF file, effectively resulting in an \"indexed TIFF\" which is described by [Manz et al. 2022](https://doi.org/10.1038/s41592-022-01482-7)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "multiplex_img_to_ome_tiff(img_arr, [\"Channel 0\"], ome_tiff_filepath)\n", - "offsets = get_offsets(ome_tiff_filepath)\n", - "with open(offsets_json_filepath, \"w\") as f:\n", - " json.dump(offsets, f)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualization of an image file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### OME-Zarr image" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"Image (ome-zarr)\")\n", - "\n", - "# Add data.\n", - "img_wrapper = ImageOmeZarrWrapper(\n", - " img_path=ome_zarr_filepath,\n", - " coordination_values={\n", - " \"fileUid\": \"image\",\n", - " }\n", - ")\n", - "segmentations_wrapper = ObsSegmentationsOmeZarrWrapper(\n", - " img_path=labels_ome_zarr_filepath,\n", - " coordination_values={\n", - " \"fileUid\": \"segmentations\",\n", - " }\n", - ")\n", - "# Here, we chain .add_object calls to add both the image and segmentation\n", - "# wrapper instances to the same dataset.\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(img_wrapper).add_object(segmentations_wrapper)\n", - "\n", - "# Add views.\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'imageLayer': CL([{\n", - " # In case there are multiple image files in our dataset,\n", - " # we use fileUid to specify which file we intend to visualize\n", - " # in this image layer.\n", - " 'fileUid': 'image',\n", - " 'photometricInterpretation': 'BlackIsZero',\n", - " 'imageChannel': CL([{\n", - " 'spatialTargetC': 0,\n", - " 'spatialChannelColor': [255, 255, 255],\n", - " 'spatialChannelWindow': [0, 4000],\n", - " }])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'segmentationLayer': CL([{\n", - " # In case there are multiple segmentation files in our dataset,\n", - " # we use fileUid to specify which file we intend to visualize\n", - " # in this segmentation layer.\n", - " 'fileUid': 'segmentations',\n", - " 'segmentationChannel': CL([{\n", - " 'obsColorEncoding': 'spatialChannelColor',\n", - " 'spatialChannelColor': [0, 255, 0],\n", - " 'spatialChannelOpacity': 0.75,\n", - " 'spatialSegmentationFilled': False,\n", - " 'spatialSegmentationStrokeWidth': 0.25,\n", - " }]),\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", - "\n", - "vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], ['cell'])\n", - "\n", - "# Layout the views.\n", - "vc.layout(hconcat(spatial, layer_controller, split=(2, 1)));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### OME-TIFF image" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"Image and segmentations (ome-tiff)\")\n", - "# Add data.\n", - "wrapper = ImageOmeTiffWrapper(\n", - " img_path=ome_tiff_filepath,\n", - " offsets_path=offsets_json_filepath\n", - ")\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(wrapper)\n", - "\n", - "# Add views.\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'imageLayer': CL([{\n", - " 'photometricInterpretation': 'BlackIsZero',\n", - " 'imageChannel': CL([{\n", - " 'spatialTargetC': 0,\n", - " 'spatialChannelColor': [255, 255, 255],\n", - " 'spatialChannelWindow': [0, 4000],\n", - " }])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "# Layout the views.\n", - "vc.layout(hconcat(spatial, layer_controller, split=(2, 1)));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.widget()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Data location options\n", - "\n", - "Vitessce can visualize data [not only stored](https://python-docs.vitessce.io/data_options.html) locally (referenced using local file or directory paths) but also stored remotely (referenced using absolute URL paths).\n", - "Depending on whether the Python kernel running the Jupyter process is running locally versus remotely (e.g., on a cluster or cloud platform such as Google Colab), certain data locations may be challenging to access from the machine running the Jupyter notebook frontend (i.e., the machine on which the web browser used to view the notebook is installed).\n", - "\n", - "To provide data via one of these alternative mechanisms, use parameters with the following suffices when instantiating the data wrapper classes.\n", - "\n", - "- `_path`: Local file or directory\n", - "- `_url`: Remote file or directory\n", - "- `_store`: Zarr-store-accessible (for zarr-based formats)\n", - "- `_artifact`: Lamin artifact\n", - "\n", - "For example, `adata_path` can be exchanged for one of the following options.\n", - "\n", - "```diff\n", - "AnnDataWrapper(\n", - "- adata_path=\"./mouse_liver.spatialdata.zarr\",\n", - "+ adata_url=\"https://example.com/mouse_liver.spatialdata.zarr\", # Absolute URL\n", - "+ adata_store=\"./mouse_liver.spatialdata.zarr\", # String interpreted as root of DirectoryStore\n", - "+ adata_store=zarr.DirectoryStore(\"./mouse_liver.spatialdata.zarr\"), # Instance of zarr.storage\n", - "+ adata_artifact=adata_zarr_artifact, # Instance of ln.Artifact\n", - " ...\n", - "```\n", - "\n", - "Note that the `_store` options are only available for Zarr-based formats, such as AnnDataWrapper, SpatialDataWrapper, and ImageOmeZarrWrapper.\n", - "Further, when multiple files are required, such as both `adata_path` and `ref_path` (for the JSON reference specification file accompanying an H5AD file) or `img_path` and `offsets_path`, multiple parameter suffices may need to be changed." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/spatial_data_mouseliver.mo.py b/docs/notebooks/spatial_data_mouseliver.mo.py new file mode 100644 index 00000000..7cef96d3 --- /dev/null +++ b/docs/notebooks/spatial_data_mouseliver.mo.py @@ -0,0 +1,729 @@ +import marimo + +__generated_with = "0.14.16" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""# Visualization of a SpatialData object and individual Spatial Elements, local data""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + This notebook explains how to create interactive visualizations of data that is accessible locally. + + + We progress through different visualization tasks, first demonstrating how Vitessce facilitates integrated imaging and spatial single-cell visualizations, then demonstrating visualization of non-spatial and image-only datasets. + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Utility dependencies""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""First, we import utility dependencies which will be used to download the example dataset and manipulate file paths, zip files, and JSON files.""") + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + import zipfile + import json + return isdir, isfile, join, json, os, urlretrieve, zipfile + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Dependencies for Vitessce""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Here, we import classes and functions from the `vitessce` Python package. + This package includes not only APIs for [visualization configuration](https://python-docs.vitessce.io/api_config.html) but also [helper functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) for basic data transformation tasks. + To specify mappings between data fields and visualization properties, the package contains [classes](https://python-docs.vitessce.io/api_data.html#module-vitessce.wrappers) which wrap standard single-cell data structures stored in formats including [AnnData](https://doi.org/10.1101/2021.12.16.473007), [SpatialData](https://doi.org/10.1038/s41592-024-02212-x), [OME-TIFF](https://doi.org/10.1007/978-3-030-23937-4_1), and [OME-Zarr](https://doi.org/10.1038/s41592-021-01326-w): + + - [AnnDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.AnnDataWrapper) + - [ImageOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeTiffWrapper) + - [ImageOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeZarrWrapper) + - [ObsSegmentationsOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeTiffWrapper) + - [ObsSegmentationsOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeZarrWrapper) + - [SpatialDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.SpatialDataWrapper) + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + SpatialDataWrapper, + AnnDataWrapper, + ImageOmeTiffWrapper, + ImageOmeZarrWrapper, + ObsSegmentationsOmeZarrWrapper, + get_initial_coordination_scope_prefix, + hconcat, + vconcat, + ) + from vitessce.data_utils import ( + VAR_CHUNK_SIZE, + generate_h5ad_ref_spec, + multiplex_img_to_ome_tiff, + multiplex_img_to_ome_zarr, + ) + return ( + AnnDataWrapper, + CL, + ImageOmeTiffWrapper, + ImageOmeZarrWrapper, + ObsSegmentationsOmeZarrWrapper, + SpatialDataWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + generate_h5ad_ref_spec, + get_initial_coordination_scope_prefix, + hconcat, + multiplex_img_to_ome_tiff, + multiplex_img_to_ome_zarr, + vt, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Dependencies for data structures""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + In this blog post, we perform basic data transformation tasks to save individual elements of an integrated SpatialData object to separate files in AnnData, OME-TIFF, and OME-Zarr formats. + To perform these data transformations, we import the following dependencies. + In general, you will typically not need to import all of these dependencies, either because you are only working with data in one of these formats, or because the data you intend to visualize is already saved to a file or directory. + + Note: Dependencies such as `spatialdata` may need to be installed before they can be imported in the next code cell. + """ + ) + return + + +@app.cell +def _(): + import numpy as np + from spatialdata import read_zarr + from spatialdata.models import Image2DModel + from anndata import AnnData + from ome_zarr.writer import write_image + import tifffile + from generate_tiff_offsets import get_offsets + return Image2DModel, get_offsets, np, read_zarr + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Download example dataset""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + We download a mouse liver dataset which serves as a SpatialData [example dataset](https://github.com/scverse/spatialdata-notebooks/blob/main/notebooks/examples/transformations.ipynb). + + This dataset was generated by [Guilliams et al.](https://doi.org/10.1016/j.cell.2021.12.018) and processed using [SPArrOW](https://doi.org/10.1101/2024.07.04.601829) during the SpatialData [developer workshop](https://doi.org/10.37044/osf.io/8ck3e) in 2024. + """ + ) + return + + +@app.cell +def _(join): + data_dir = "data" + zip_filepath = join(data_dir, "mouse_liver.spatialdata.zarr.zip") + spatialdata_filepath = join(data_dir, "mouse_liver.spatialdata.zarr") + adata_zarr_filepath = join(data_dir, "mouse_liver.anndata.zarr") + adata_h5ad_filepath = join(data_dir, "mouse_liver.h5ad") + ref_spec_json_filepath = join(data_dir, "mouse_liver.h5ad.ref.json") + ome_tiff_filepath = join(data_dir, "mouse_liver.ome.tif") + offsets_json_filepath = join(data_dir, "mouse_liver.ome.tif.offsets.json") + ome_zarr_filepath = join(data_dir, "mouse_liver.ome.zarr") + labels_ome_zarr_filepath = join(data_dir, "mouse_liver.labels.ome.zarr") + return ( + adata_h5ad_filepath, + adata_zarr_filepath, + data_dir, + labels_ome_zarr_filepath, + offsets_json_filepath, + ome_tiff_filepath, + ome_zarr_filepath, + ref_spec_json_filepath, + spatialdata_filepath, + zip_filepath, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""The following code uses Python's `urlretrieve` to download the SpatialData object as a zip file, then unzips the file using the `zipfile` module.""") + return + + +@app.cell +def _( + data_dir, + isdir, + isfile, + join, + os, + spatialdata_filepath, + urlretrieve, + zip_filepath, + zipfile, +): + if not isdir(spatialdata_filepath): + if not isfile(zip_filepath): + os.makedirs(data_dir, exist_ok=True) + urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/mouse_liver.zip', zip_filepath) + with zipfile.ZipFile(zip_filepath,"r") as zip_ref: + zip_ref.extractall(data_dir) + os.rename(join(data_dir, "data.zarr"), spatialdata_filepath) + return + + +@app.cell +def _(read_zarr, spatialdata_filepath): + sdata = read_zarr(spatialdata_filepath) + sdata + return (sdata,) + + +@app.cell +def _(mo): + mo.md( + r""" + ## Add image pyramid with `scale_factor`s of 2 + + Vitessce assumes pyramidal (multi-resolution) images have subsequent resolutions scaled by factors of 2. + + In this case, `sdata.images['raw_image']` has 6432x6432 pixels in the highest resolution and 1608x1608 pixels in the next resolution, a scale factor of 4 (rather than 2). + """ + ) + return + + +@app.cell +def _(sdata): + img_arr2 = sdata.images['raw_image'].scale0.image.to_numpy() + img_arr2 + return (img_arr2,) + + +@app.cell +def _(Image2DModel, img_arr2, sdata): + sdata['raw_image2'] = Image2DModel.parse( + data=img_arr2, + dims=['c', 'y', 'x'], + scale_factors=[2, 2], + ) + try: + sdata.write_element("raw_image2") + except: + pass + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Visualization of a SpatialData object""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + SpatialData objects are the most complex type of data structure we will work with in this blog post. + SpatialData objects function as contains for multiple types of Spatial Elements: + + - Tables (each table is represented as an AnnData object) + - Points (e.g., coordinates of transcripts from FISH-based experiments) + - Shapes (vector-based shapes such as polygons and circles) + - Labels (label images, i.e., segmentation bitmasks; each label image is stored using OME-Zarr) + - Images (microscopy images; each image is stored using OME-Zarr) + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + To visualize data stored in a SpatialData object, we use the `SpatialDataWrapper` class and specify the paths (relative to the root of the Zarr [directory store](https://zarr.readthedocs.io/en/v2.18.5/api/storage.html#zarr.storage.DirectoryStore)) to different spatial elements of interest. + """ + ) + return + + +@app.cell +def _( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + spatialdata_filepath, +): + vc = VitessceConfig(schema_version='1.0.18', name='SpatialData Demo') + _wrapper = SpatialDataWrapper( + sdata_path=spatialdata_filepath, + table_path='tables/table', + image_path='images/raw_image2', + obs_segmentations_path='labels/segmentation_mask', + obs_feature_matrix_path='tables/table/X', + obs_set_paths=['tables/table/obs/annotation'], + obs_set_names=['Annotation'], + region='nucleus_boundaries', + coordinate_system='global', + coordination_values={'obsType': 'cell'} + ) + _dataset = vc.add_dataset(name='Mouse Liver').add_object(_wrapper) + + _spatial = vc.add_view('spatialBeta', dataset=_dataset) + feature_list = vc.add_view('featureList', dataset=_dataset) + _layer_controller = vc.add_view('layerControllerBeta', dataset=_dataset) + obs_sets = vc.add_view('obsSets', dataset=_dataset) + _heatmap = vc.add_view('heatmap', dataset=_dataset) + + [obs_color_encoding_scope] = vc.add_coordination('obsColorEncoding') + obs_color_encoding_scope.set_value('cellSetSelection') + vc.link_views_by_dict([_spatial, _layer_controller], { + 'imageLayer': CL([{ + 'photometricInterpretation': 'BlackIsZero', + 'imageChannel': CL([{ + 'spatialTargetC': 0, + 'spatialChannelColor': [255, 255, 255], + 'spatialChannelWindow': [0, 4000] + }]) + }]) + }, scope_prefix=get_initial_coordination_scope_prefix('A', 'image')) + + vc.link_views_by_dict([_spatial, _layer_controller], { + 'segmentationLayer': CL([{ + 'segmentationChannel': CL([{ + 'obsColorEncoding': obs_color_encoding_scope + }]) + }]) + }, scope_prefix=get_initial_coordination_scope_prefix('A', 'obsSegmentations')) + + vc.link_views([_spatial, _layer_controller, feature_list, obs_sets, _heatmap], ['obsType'], [_wrapper.obs_type_label]) + + vc.link_views_by_dict([feature_list, obs_sets, _heatmap], { + 'obsColorEncoding': obs_color_encoding_scope + }, meta=False) + vc.layout(_spatial / _heatmap | _layer_controller / (feature_list | obs_sets)) + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### Render the widget""") + return + + +@app.cell +def _(vc): + _vw = vc.widget() + _vw + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Extract AnnData object from SpatialData object""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + The above example demonstrates how to visualize a spatial 'omics dataset containing not only single-cell information (e.g., a cell-by-gene expression matrix, cell type annotations) but also an image and cell segmentations. + To demonstrate how to use Vitessce to visualize data from a (non-spatial) single-cell experiment, we will extract this information from the SpatialData object and save it to a simpler [AnnData](https://anndata.readthedocs.io/) object (ignoring the imaging and spatially-resolved elements). + """ + ) + return + + +@app.cell +def _(sdata): + adata = sdata.tables['table'] + adata + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + As Zarr-formatted data can be easily visualized by Vitessce, we recommend saving the AnnData object to a Zarr store using the [write_zarr](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_zarr.html) method. + Optionally, the shape of array chunks (for the AnnData `X` array) can be specified as a parameter, to optimize performance based on data access patterns. + For example, a common pattern is to visualize data across all cells for one gene. + To support such a pattern, the chunk shape can be specified as follows, `(total number of cells, small number of genes)`, resulting in tall-and-skinny array chunks. + """ + ) + return + + +@app.cell +def _(VAR_CHUNK_SIZE, adata, adata_zarr_filepath): + adata.write_zarr(adata_zarr_filepath, chunks=(adata.shape[0], VAR_CHUNK_SIZE)) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Alternatively, your AnnData object may already be stored using the H5AD (HDF5-based) format. + To demonstrate this scenario, we save the object using the [write_h5ad](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_h5ad.html) method. + """ + ) + return + + +@app.cell +def _(adata, adata_h5ad_filepath): + adata.write_h5ad(adata_h5ad_filepath) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""To read H5AD-formatted data, Vitessce requires an accompanying JSON [references specification](https://fsspec.github.io/kerchunk/spec.html) file, which can be constructed using the `generate_h5ad_ref_spec` utility function.""") + return + + +@app.cell +def _( + adata_h5ad_filepath, + generate_h5ad_ref_spec, + json, + ref_spec_json_filepath, +): + ref_dict = generate_h5ad_ref_spec(adata_h5ad_filepath) + with open(ref_spec_json_filepath, 'w') as _f: + json.dump(ref_dict, _f) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Visualization of an AnnData object""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### Zarr-based AnnData""") + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, adata_zarr_filepath, vt): + vc_1 = VitessceConfig(schema_version='1.0.18', name='AnnData (zarr)') + _wrapper = AnnDataWrapper(adata_path=adata_zarr_filepath, obs_feature_matrix_path='X', obs_set_paths=['obs/annotation'], obs_set_names=['Annotation'], coordination_values={'obsType': 'cell'}) + _dataset = vc_1.add_dataset(name='Mouse Liver').add_object(_wrapper) + _heatmap = vc_1.add_view(vt.HEATMAP, dataset=_dataset) + feature_list_1 = vc_1.add_view(vt.FEATURE_LIST, dataset=_dataset) + obs_sets_1 = vc_1.add_view(vt.OBS_SETS, dataset=_dataset) + _violin_plots = vc_1.add_view('obsSetFeatureValueDistribution', dataset=_dataset) + vc_1.link_views([_heatmap, feature_list_1, obs_sets_1], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]]) + vc_1.layout(_heatmap / _violin_plots | feature_list_1 / obs_sets_1) + return (vc_1,) + + +@app.cell +def _(vc_1): + vc_1.widget() + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### H5AD-based AnnData""") + return + + +@app.cell +def _( + AnnDataWrapper, + VitessceConfig, + adata_h5ad_filepath, + ref_spec_json_filepath, + vt, +): + vc_2 = VitessceConfig(schema_version='1.0.18', name='AnnData (h5ad)') + _wrapper = AnnDataWrapper(adata_path=adata_h5ad_filepath, ref_path=ref_spec_json_filepath, obs_feature_matrix_path='X', obs_set_paths=['obs/annotation'], obs_set_names=['Annotation'], coordination_values={'obsType': 'cell'}) + _dataset = vc_2.add_dataset(name='Mouse Liver').add_object(_wrapper) + _heatmap = vc_2.add_view(vt.HEATMAP, dataset=_dataset) + feature_list_2 = vc_2.add_view(vt.FEATURE_LIST, dataset=_dataset) + obs_sets_2 = vc_2.add_view(vt.OBS_SETS, dataset=_dataset) + _violin_plots = vc_2.add_view('obsSetFeatureValueDistribution', dataset=_dataset) + vc_2.link_views([_heatmap, feature_list_2, obs_sets_2], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]]) + vc_2.layout(_heatmap / _violin_plots | feature_list_2 / obs_sets_2) + return feature_list_2, obs_sets_2, vc_2 + + +@app.cell +def _(vc_2): + vc_2.widget() + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Extract image from SpatialData object""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""In contrast to extraction of the non-spatial data from the SpatialData object, we can extract the imaging (and segmentation/label image) data and save it to a dedicated bioimaging file format.""") + return + + +@app.cell +def _(np, sdata): + img_arr = sdata.images['raw_image'].scale0.image.to_numpy() + labels_arr = sdata.labels['segmentation_mask'].to_numpy() + labels_arr = labels_arr[np.newaxis, :] + return img_arr, labels_arr + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### Save image to OME-Zarr""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + For small images, the data can be saved to OME-Zarr or OME-TIFF format using [utility functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) from the Vitessce package. + Larger images require generation of an [image pyramid](https://en.wikipedia.org/wiki/Pyramid_(image_processing)), which can be performed using tools from the OME ecosystem such as [bioformats2raw](https://github.com/glencoesoftware/bioformats2raw) and [raw2ometiff](https://github.com/glencoesoftware/raw2ometiff). + """ + ) + return + + +@app.cell +def _( + img_arr, + labels_arr, + labels_ome_zarr_filepath, + multiplex_img_to_ome_zarr, + ome_zarr_filepath, +): + multiplex_img_to_ome_zarr(img_arr, ["Channel 0"], ome_zarr_filepath) + multiplex_img_to_ome_zarr(labels_arr, ["cell"], labels_ome_zarr_filepath) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### Save image and segmentations to OME-TIFF""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + To efficiently visualize OME-TIFF data using Vitessce, a JSON-based offsets file can be constructed using [generate-tiff-offsets](https://github.com/hms-dbmi/generate-tiff-offsets). + This JSON file contains byte offsets into different partitions of the TIFF file, effectively resulting in an "indexed TIFF" which is described by [Manz et al. 2022](https://doi.org/10.1038/s41592-022-01482-7). + """ + ) + return + + +@app.cell +def _( + get_offsets, + img_arr, + json, + multiplex_img_to_ome_tiff, + offsets_json_filepath, + ome_tiff_filepath, +): + multiplex_img_to_ome_tiff(img_arr, ['Channel 0'], ome_tiff_filepath) + offsets = get_offsets(ome_tiff_filepath) + with open(offsets_json_filepath, 'w') as _f: + json.dump(offsets, _f) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Visualization of an image file""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### OME-Zarr image""") + return + + +@app.cell +def _( + CL, + ImageOmeZarrWrapper, + ObsSegmentationsOmeZarrWrapper, + VitessceConfig, + feature_list_2, + get_initial_coordination_scope_prefix, + hconcat, + labels_ome_zarr_filepath, + obs_sets_2, + ome_zarr_filepath, +): + vc_3 = VitessceConfig(schema_version='1.0.18', name='Image (ome-zarr)') + img_wrapper = ImageOmeZarrWrapper(img_path=ome_zarr_filepath, coordination_values={'fileUid': 'image'}) + segmentations_wrapper = ObsSegmentationsOmeZarrWrapper(img_path=labels_ome_zarr_filepath, coordination_values={'fileUid': 'segmentations'}) + _dataset = vc_3.add_dataset(name='Mouse Liver').add_object(img_wrapper).add_object(segmentations_wrapper) + _spatial = vc_3.add_view('spatialBeta', dataset=_dataset) + _layer_controller = vc_3.add_view('layerControllerBeta', dataset=_dataset) + vc_3.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'fileUid': 'image', 'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image')) + vc_3.link_views_by_dict([_spatial, _layer_controller], {'segmentationLayer': CL([{'fileUid': 'segmentations', 'segmentationChannel': CL([{'obsColorEncoding': 'spatialChannelColor', 'spatialChannelColor': [0, 255, 0], 'spatialChannelOpacity': 0.75, 'spatialSegmentationFilled': False, 'spatialSegmentationStrokeWidth': 0.25}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'obsSegmentations')) + vc_3.link_views([_spatial, _layer_controller, feature_list_2, obs_sets_2], ['obsType'], ['cell']) + vc_3.layout(hconcat(_spatial, _layer_controller, split=(2, 1))) + return (vc_3,) + + +@app.cell +def _(vc_3): + _vw = vc_3.widget() + _vw + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### OME-TIFF image""") + return + + +@app.cell +def _( + CL, + ImageOmeTiffWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + hconcat, + offsets_json_filepath, + ome_tiff_filepath, +): + vc_4 = VitessceConfig(schema_version='1.0.18', name='Image and segmentations (ome-tiff)') + _wrapper = ImageOmeTiffWrapper(img_path=ome_tiff_filepath, offsets_path=offsets_json_filepath) + _dataset = vc_4.add_dataset(name='Mouse Liver').add_object(_wrapper) + _spatial = vc_4.add_view('spatialBeta', dataset=_dataset) + _layer_controller = vc_4.add_view('layerControllerBeta', dataset=_dataset) + vc_4.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image')) + vc_4.layout(hconcat(_spatial, _layer_controller, split=(2, 1))) + return (vc_4,) + + +@app.cell +def _(vc_4): + vc_4.widget() + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Data location options + + Vitessce can visualize data [not only stored](https://python-docs.vitessce.io/data_options.html) locally (referenced using local file or directory paths) but also stored remotely (referenced using absolute URL paths). + Depending on whether the Python kernel running the Jupyter process is running locally versus remotely (e.g., on a cluster or cloud platform such as Google Colab), certain data locations may be challenging to access from the machine running the Jupyter notebook frontend (i.e., the machine on which the web browser used to view the notebook is installed). + + To provide data via one of these alternative mechanisms, use parameters with the following suffices when instantiating the data wrapper classes. + + - `_path`: Local file or directory + - `_url`: Remote file or directory + - `_store`: Zarr-store-accessible (for zarr-based formats) + - `_artifact`: Lamin artifact + + For example, `adata_path` can be exchanged for one of the following options. + + ```diff + AnnDataWrapper( + - adata_path="./mouse_liver.spatialdata.zarr", + + adata_url="https://example.com/mouse_liver.spatialdata.zarr", # Absolute URL + + adata_store="./mouse_liver.spatialdata.zarr", # String interpreted as root of DirectoryStore + + adata_store=zarr.DirectoryStore("./mouse_liver.spatialdata.zarr"), # Instance of zarr.storage + + adata_artifact=adata_zarr_artifact, # Instance of ln.Artifact + ... + ``` + + Note that the `_store` options are only available for Zarr-based formats, such as AnnDataWrapper, SpatialDataWrapper, and ImageOmeZarrWrapper. + Further, when multiple files are required, such as both `adata_path` and `ref_path` (for the JSON reference specification file accompanying an H5AD file) or `img_path` and `offsets_path`, multiple parameter suffices may need to be changed. + """ + ) + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/spatial_data_mouseliver_remote.ipynb b/docs/notebooks/spatial_data_mouseliver_remote.ipynb deleted file mode 100644 index d8bdb0dd..00000000 --- a/docs/notebooks/spatial_data_mouseliver_remote.ipynb +++ /dev/null @@ -1,627 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden", - "tags": [] - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "# Visualization of a SpatialData object and individual Spatial Elements, remote data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook explains how to create interactive visualizations of data that are accessible remotely.\n", - "\n", - "\n", - "We progress through different visualization tasks, first demonstrating how Vitessce facilitates integrated imaging and spatial single-cell visualizations, then demonstrating visualization of non-spatial and image-only datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Utility dependencies" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, we import utility dependencies which will be used to download the example dataset and manipulate file paths, zip files, and JSON files." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "import zipfile\n", - "import json" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dependencies for Vitessce" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we import classes and functions from the `vitessce` Python package.\n", - "This package includes not only APIs for [visualization configuration](https://python-docs.vitessce.io/api_config.html) but also [helper functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) for basic data transformation tasks.\n", - "To specify mappings between data fields and visualization properties, the package contains [classes](https://python-docs.vitessce.io/api_data.html#module-vitessce.wrappers) which wrap standard single-cell data structures stored in formats including [AnnData](https://doi.org/10.1101/2021.12.16.473007), [SpatialData](https://doi.org/10.1038/s41592-024-02212-x), [OME-TIFF](https://doi.org/10.1007/978-3-030-23937-4_1), and [OME-Zarr](https://doi.org/10.1038/s41592-021-01326-w):\n", - "\n", - "- [AnnDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.AnnDataWrapper)\n", - "- [ImageOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeTiffWrapper)\n", - "- [ImageOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeZarrWrapper)\n", - "- [ObsSegmentationsOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeTiffWrapper)\n", - "- [ObsSegmentationsOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeZarrWrapper)\n", - "- [SpatialDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.SpatialDataWrapper)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - " SpatialDataWrapper,\n", - " AnnDataWrapper,\n", - " ImageOmeTiffWrapper,\n", - " ImageOmeZarrWrapper,\n", - " ObsSegmentationsOmeZarrWrapper,\n", - " get_initial_coordination_scope_prefix,\n", - " hconcat,\n", - " vconcat,\n", - ")\n", - "from vitessce.data_utils import (\n", - " VAR_CHUNK_SIZE,\n", - " generate_h5ad_ref_spec,\n", - " multiplex_img_to_ome_tiff,\n", - " multiplex_img_to_ome_zarr,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this blog post, we perform basic data transformation tasks to save individual elements of an integrated SpatialData object to separate files in AnnData, OME-TIFF, and OME-Zarr formats.\n", - "To perform these data transformations, we import the following dependencies.\n", - "In general, you will typically not need to import all of these dependencies, either because you are only working with data in one of these formats, or because the data you intend to visualize is already saved to a file or directory." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Download example dataset" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We download a mouse liver dataset which serves as a SpatialData [example dataset](https://github.com/scverse/spatialdata-notebooks/blob/main/notebooks/examples/transformations.ipynb).\n", - "\n", - "This dataset was generated by [Guilliams et al.](https://doi.org/10.1016/j.cell.2021.12.018) and processed using [SPArrOW](https://doi.org/10.1101/2024.07.04.601829) during the SpatialData [developer workshop](https://doi.org/10.37044/osf.io/8ck3e) in 2024." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "base_url = \"https://storage.googleapis.com/vitessce-demo-data/spatialdata-may-2025\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zip_filepath = f\"{base_url}/mouse_liver.spatialdata.zarr.zip\"\n", - "spatialdata_filepath = f\"{base_url}/mouse_liver.spatialdata.zarr\"\n", - "adata_zarr_filepath = f\"{base_url}/mouse_liver.anndata.zarr\"\n", - "adata_h5ad_filepath = f\"{base_url}/mouse_liver.h5ad\"\n", - "ref_spec_json_filepath = f\"{base_url}/mouse_liver.h5ad.ref.json\"\n", - "ome_tiff_filepath = f\"{base_url}/mouse_liver.ome.tif\"\n", - "offsets_json_filepath = f\"{base_url}/mouse_liver.ome.tif.offsets.json\"\n", - "ome_zarr_filepath = f\"{base_url}/mouse_liver.ome.zarr\"\n", - "labels_ome_zarr_filepath = f\"{base_url}/mouse_liver.labels.ome.zarr\"" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## Visualization of a SpatialData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "SpatialData objects are the most complex type of data structure we will work with in this blog post.\n", - "SpatialData objects function as contains for multiple types of Spatial Elements:\n", - "\n", - "- Tables (each table is represented as an AnnData object)\n", - "- Points (e.g., coordinates of transcripts from FISH-based experiments)\n", - "- Shapes (vector-based shapes such as polygons and circles)\n", - "- Labels (label images, i.e., segmentation bitmasks; each label image is stored using OME-Zarr)\n", - "- Images (microscopy images; each image is stored using OME-Zarr)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views.\n", - "To visualize data stored in a SpatialData object, we use the `SpatialDataWrapper` class and specify the paths (relative to the root of the Zarr [directory store](https://zarr.readthedocs.io/en/v2.18.5/api/storage.html#zarr.storage.DirectoryStore)) to different spatial elements of interest." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create a VitessceConfig instance.\n", - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"SpatialData Demo\")\n", - "\n", - "# Instantiate the wrapper class, specifying data fields of interest.\n", - "wrapper = SpatialDataWrapper(\n", - " sdata_url=spatialdata_filepath,\n", - " # The following paths are relative to the root of the SpatialData Zarr store on-disk.\n", - " table_path=\"tables/table\",\n", - " image_path=\"images/raw_image\",\n", - " obs_segmentations_path=\"labels/segmentation_mask\",\n", - " obs_feature_matrix_path=\"tables/table/X\",\n", - " obs_set_paths=[\"tables/table/obs/annotation\"],\n", - " obs_set_names=[\"Annotation\"],\n", - " region=\"nucleus_boundaries\",\n", - " coordinate_system=\"global\",\n", - " coordination_values={\n", - " \"obsType\": \"cell\" \n", - " }\n", - ")\n", - "# Add a new dataset to the Vitessce configuration,\n", - "# then add the wrapper class instance to this dataset.\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(wrapper)\n", - "\n", - "# Add views (visualizations) to the configuration.\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "feature_list = vc.add_view(\"featureList\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "obs_sets = vc.add_view(\"obsSets\", dataset=dataset)\n", - "heatmap = vc.add_view(\"heatmap\", dataset=dataset)\n", - "\n", - "[obs_color_encoding_scope] = vc.add_coordination(\"obsColorEncoding\")\n", - "obs_color_encoding_scope.set_value(\"cellSetSelection\")\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " \"imageLayer\": CL([{\n", - " \"photometricInterpretation\": \"BlackIsZero\",\n", - " \"imageChannel\": CL([{\n", - " \"spatialTargetC\": 0,\n", - " \"spatialChannelColor\": [255, 255, 255],\n", - " \"spatialChannelWindow\": [0, 4000],\n", - " }])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " \"segmentationLayer\": CL([{\n", - " \"segmentationChannel\": CL([{\n", - " \"obsColorEncoding\": obs_color_encoding_scope,\n", - " }]),\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", - "\n", - "vc.link_views([spatial, layer_controller, feature_list, obs_sets, heatmap], [\"obsType\"], [wrapper.obs_type_label])\n", - "vc.link_views_by_dict([feature_list, obs_sets, heatmap], {\n", - " \"obsColorEncoding\": obs_color_encoding_scope,\n", - "}, meta=False)\n", - "\n", - "# Layout the views in a grid arrangement.\n", - "vc.layout((spatial / heatmap) | (layer_controller / (feature_list | obs_sets)));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Render the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Extract AnnData object from SpatialData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The above example demonstrates how to visualize a spatial 'omics dataset containing not only single-cell information (e.g., a cell-by-gene expression matrix, cell type annotations) but also an image and cell segmentations.\n", - "To demonstrate how to use Vitessce to visualize data from a (non-spatial) single-cell experiment, we will extract this information from the SpatialData object and save it to a simpler [AnnData](https://anndata.readthedocs.io/) object (ignoring the imaging and spatially-resolved elements)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As Zarr-formatted data can be easily visualized by Vitessce, we recommend saving the AnnData object to a Zarr store using the [write_zarr](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_zarr.html) method.\n", - "Optionally, the shape of array chunks (for the AnnData `X` array) can be specified as a parameter, to optimize performance based on data access patterns.\n", - "For example, a common pattern is to visualize data across all cells for one gene.\n", - "To support such a pattern, the chunk shape can be specified as follows, `(total number of cells, small number of genes)`, resulting in tall-and-skinny array chunks." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternatively, your AnnData object may already be stored using the H5AD (HDF5-based) format.\n", - "To demonstrate this scenario, we save the object using the [write_h5ad](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_h5ad.html) method." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To read H5AD-formatted data, Vitessce requires an accompanying JSON [references specification](https://fsspec.github.io/kerchunk/spec.html) file, which can be constructed using the `generate_h5ad_ref_spec` utility function." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualization of an AnnData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Zarr-based AnnData" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"AnnData (zarr)\")\n", - "# Add data.\n", - "wrapper = AnnDataWrapper(\n", - " adata_url=adata_zarr_filepath,\n", - " obs_feature_matrix_path=\"X\",\n", - " obs_set_paths=[\"obs/annotation\"],\n", - " obs_set_names=[\"Annotation\"],\n", - " coordination_values={\n", - " \"obsType\": \"cell\" \n", - " }\n", - ")\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(wrapper)\n", - "\n", - "# Add views.\n", - "heatmap = vc.add_view(vt.HEATMAP, dataset=dataset)\n", - "feature_list = vc.add_view(vt.FEATURE_LIST, dataset=dataset)\n", - "obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", - "violin_plots = vc.add_view(\"obsSetFeatureValueDistribution\", dataset=dataset)\n", - "\n", - "vc.link_views([heatmap, feature_list, obs_sets], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]])\n", - "\n", - "# Layout the views.\n", - "vc.layout((heatmap / violin_plots) | (feature_list / obs_sets));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.widget()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### H5AD-based AnnData" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"AnnData (h5ad)\")\n", - "# Add data.\n", - "wrapper = AnnDataWrapper(\n", - " adata_url=adata_h5ad_filepath,\n", - " ref_url=ref_spec_json_filepath,\n", - " obs_feature_matrix_path=\"X\",\n", - " obs_set_paths=[\"obs/annotation\"],\n", - " obs_set_names=[\"Annotation\"],\n", - " coordination_values={\n", - " \"obsType\": \"cell\" \n", - " }\n", - ")\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(wrapper)\n", - "\n", - "# Add views.\n", - "heatmap = vc.add_view(vt.HEATMAP, dataset=dataset)\n", - "feature_list = vc.add_view(vt.FEATURE_LIST, dataset=dataset)\n", - "obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", - "violin_plots = vc.add_view(\"obsSetFeatureValueDistribution\", dataset=dataset)\n", - "\n", - "vc.link_views([heatmap, feature_list, obs_sets], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]])\n", - "\n", - "# Layout the views.\n", - "vc.layout((heatmap / violin_plots) | (feature_list / obs_sets));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.widget()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualization of an image file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### OME-Zarr image" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"Image (ome-zarr)\")\n", - "\n", - "# Add data.\n", - "img_wrapper = ImageOmeZarrWrapper(\n", - " img_url=ome_zarr_filepath,\n", - " coordination_values={\n", - " \"fileUid\": \"image\",\n", - " }\n", - ")\n", - "segmentations_wrapper = ObsSegmentationsOmeZarrWrapper(\n", - " img_url=labels_ome_zarr_filepath,\n", - " coordination_values={\n", - " \"fileUid\": \"segmentations\",\n", - " }\n", - ")\n", - "# Here, we chain .add_object calls to add both the image and segmentation\n", - "# wrapper instances to the same dataset.\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(img_wrapper).add_object(segmentations_wrapper)\n", - "\n", - "# Add views.\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'imageLayer': CL([{\n", - " # In case there are multiple image files in our dataset,\n", - " # we use fileUid to specify which file we intend to visualize\n", - " # in this image layer.\n", - " 'fileUid': 'image',\n", - " 'photometricInterpretation': 'BlackIsZero',\n", - " 'imageChannel': CL([{\n", - " 'spatialTargetC': 0,\n", - " 'spatialChannelColor': [255, 255, 255],\n", - " 'spatialChannelWindow': [0, 4000],\n", - " }])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'segmentationLayer': CL([{\n", - " # In case there are multiple segmentation files in our dataset,\n", - " # we use fileUid to specify which file we intend to visualize\n", - " # in this segmentation layer.\n", - " 'fileUid': 'segmentations',\n", - " 'segmentationChannel': CL([{\n", - " 'obsColorEncoding': 'spatialChannelColor',\n", - " 'spatialChannelColor': [0, 255, 0],\n", - " 'spatialChannelOpacity': 0.75,\n", - " 'spatialSegmentationFilled': False,\n", - " 'spatialSegmentationStrokeWidth': 0.25,\n", - " }]),\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", - "\n", - "vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], ['cell'])\n", - "\n", - "# Layout the views.\n", - "vc.layout(hconcat(spatial, layer_controller, split=(2, 1)));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### OME-TIFF image" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.18\", name=\"Image and segmentations (ome-tiff)\")\n", - "# Add data.\n", - "wrapper = ImageOmeTiffWrapper(\n", - " img_url=ome_tiff_filepath,\n", - " offsets_url=offsets_json_filepath\n", - ")\n", - "dataset = vc.add_dataset(name='Mouse Liver').add_object(wrapper)\n", - "\n", - "# Add views.\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'imageLayer': CL([{\n", - " 'photometricInterpretation': 'BlackIsZero',\n", - " 'imageChannel': CL([{\n", - " 'spatialTargetC': 0,\n", - " 'spatialChannelColor': [255, 255, 255],\n", - " 'spatialChannelWindow': [0, 4000],\n", - " }])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "# Layout the views.\n", - "vc.layout(hconcat(spatial, layer_controller, split=(2, 1)));" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.widget()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Data location options\n", - "\n", - "Vitessce can visualize data [not only stored](https://python-docs.vitessce.io/data_options.html) locally (referenced using local file or directory paths) but also stored remotely (referenced using absolute URL paths).\n", - "Depending on whether the Python kernel running the Jupyter process is running locally versus remotely (e.g., on a cluster or cloud platform such as Google Colab), certain data locations may be challenging to access from the machine running the Jupyter notebook frontend (i.e., the machine on which the web browser used to view the notebook is installed).\n", - "\n", - "To provide data via one of these alternative mechanisms, use parameters with the following suffices when instantiating the data wrapper classes.\n", - "\n", - "- `_path`: Local file or directory\n", - "- `_url`: Remote file or directory\n", - "- `_store`: Zarr-store-accessible (for zarr-based formats)\n", - "- `_artifact`: Lamin artifact\n", - "\n", - "For example, `adata_path` can be exchanged for one of the following options.\n", - "\n", - "```diff\n", - "AnnDataWrapper(\n", - "- adata_path=\"./mouse_liver.spatialdata.zarr\",\n", - "+ adata_url=\"https://example.com/mouse_liver.spatialdata.zarr\", # Absolute URL\n", - "+ adata_store=\"./mouse_liver.spatialdata.zarr\", # String interpreted as root of DirectoryStore\n", - "+ adata_store=zarr.DirectoryStore(\"./mouse_liver.spatialdata.zarr\"), # Instance of zarr.storage\n", - "+ adata_artifact=adata_zarr_artifact, # Instance of ln.Artifact\n", - " ...\n", - "```\n", - "\n", - "Note that the `_store` options are only available for Zarr-based formats, such as AnnDataWrapper, SpatialDataWrapper, and ImageOmeZarrWrapper.\n", - "Further, when multiple files are required, such as both `adata_path` and `ref_path` (for the JSON reference specification file accompanying an H5AD file) or `img_path` and `offsets_path`, multiple parameter suffices may need to be changed." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/spatial_data_mouseliver_remote.mo.py b/docs/notebooks/spatial_data_mouseliver_remote.mo.py new file mode 100644 index 00000000..4e9dba66 --- /dev/null +++ b/docs/notebooks/spatial_data_mouseliver_remote.mo.py @@ -0,0 +1,496 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App(width="full") + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""# Vitessce Widget Tutorial""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""# Visualization of a SpatialData object and individual Spatial Elements, remote data""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + This notebook explains how to create interactive visualizations of data that are accessible remotely. + + + We progress through different visualization tasks, first demonstrating how Vitessce facilitates integrated imaging and spatial single-cell visualizations, then demonstrating visualization of non-spatial and image-only datasets. + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Utility dependencies""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""First, we import utility dependencies which will be used to download the example dataset and manipulate file paths, zip files, and JSON files.""") + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + import zipfile + import json + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Dependencies for Vitessce""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Here, we import classes and functions from the `vitessce` Python package. + This package includes not only APIs for [visualization configuration](https://python-docs.vitessce.io/api_config.html) but also [helper functions](https://python-docs.vitessce.io/api_data.html#vitessce-data-utils) for basic data transformation tasks. + To specify mappings between data fields and visualization properties, the package contains [classes](https://python-docs.vitessce.io/api_data.html#module-vitessce.wrappers) which wrap standard single-cell data structures stored in formats including [AnnData](https://doi.org/10.1101/2021.12.16.473007), [SpatialData](https://doi.org/10.1038/s41592-024-02212-x), [OME-TIFF](https://doi.org/10.1007/978-3-030-23937-4_1), and [OME-Zarr](https://doi.org/10.1038/s41592-021-01326-w): + + - [AnnDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.AnnDataWrapper) + - [ImageOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeTiffWrapper) + - [ImageOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ImageOmeZarrWrapper) + - [ObsSegmentationsOmeTiffWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeTiffWrapper) + - [ObsSegmentationsOmeZarrWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.ObsSegmentationsOmeZarrWrapper) + - [SpatialDataWrapper](https://python-docs.vitessce.io/api_data.html#vitessce.wrappers.SpatialDataWrapper) + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + SpatialDataWrapper, + AnnDataWrapper, + ImageOmeTiffWrapper, + ImageOmeZarrWrapper, + ObsSegmentationsOmeZarrWrapper, + get_initial_coordination_scope_prefix, + hconcat, + vconcat, + ) + from vitessce.data_utils import ( + VAR_CHUNK_SIZE, + generate_h5ad_ref_spec, + multiplex_img_to_ome_tiff, + multiplex_img_to_ome_zarr, + ) + return ( + AnnDataWrapper, + CL, + ImageOmeTiffWrapper, + ImageOmeZarrWrapper, + ObsSegmentationsOmeZarrWrapper, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + hconcat, + vt, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + In this blog post, we perform basic data transformation tasks to save individual elements of an integrated SpatialData object to separate files in AnnData, OME-TIFF, and OME-Zarr formats. + To perform these data transformations, we import the following dependencies. + In general, you will typically not need to import all of these dependencies, either because you are only working with data in one of these formats, or because the data you intend to visualize is already saved to a file or directory. + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Download example dataset""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + We download a mouse liver dataset which serves as a SpatialData [example dataset](https://github.com/scverse/spatialdata-notebooks/blob/main/notebooks/examples/transformations.ipynb). + + This dataset was generated by [Guilliams et al.](https://doi.org/10.1016/j.cell.2021.12.018) and processed using [SPArrOW](https://doi.org/10.1101/2024.07.04.601829) during the SpatialData [developer workshop](https://doi.org/10.37044/osf.io/8ck3e) in 2024. + """ + ) + return + + +@app.cell +def _(): + base_url = "https://storage.googleapis.com/vitessce-demo-data/spatialdata-may-2025" + return (base_url,) + + +@app.cell +def _(base_url): + zip_filepath = f"{base_url}/mouse_liver.spatialdata.zarr.zip" + spatialdata_filepath = f"{base_url}/mouse_liver.spatialdata.zarr" + adata_zarr_filepath = f"{base_url}/mouse_liver.anndata.zarr" + adata_h5ad_filepath = f"{base_url}/mouse_liver.h5ad" + ref_spec_json_filepath = f"{base_url}/mouse_liver.h5ad.ref.json" + ome_tiff_filepath = f"{base_url}/mouse_liver.ome.tif" + offsets_json_filepath = f"{base_url}/mouse_liver.ome.tif.offsets.json" + ome_zarr_filepath = f"{base_url}/mouse_liver.ome.zarr" + labels_ome_zarr_filepath = f"{base_url}/mouse_liver.labels.ome.zarr" + return ( + adata_h5ad_filepath, + adata_zarr_filepath, + labels_ome_zarr_filepath, + offsets_json_filepath, + ome_tiff_filepath, + ome_zarr_filepath, + ref_spec_json_filepath, + spatialdata_filepath, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Visualization of a SpatialData object""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + SpatialData objects are the most complex type of data structure we will work with in this blog post. + SpatialData objects function as contains for multiple types of Spatial Elements: + + - Tables (each table is represented as an AnnData object) + - Points (e.g., coordinates of transcripts from FISH-based experiments) + - Shapes (vector-based shapes such as polygons and circles) + - Labels (label images, i.e., segmentation bitmasks; each label image is stored using OME-Zarr) + - Images (microscopy images; each image is stored using OME-Zarr) + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + To visualize data stored in a SpatialData object, we use the `SpatialDataWrapper` class and specify the paths (relative to the root of the Zarr [directory store](https://zarr.readthedocs.io/en/v2.18.5/api/storage.html#zarr.storage.DirectoryStore)) to different spatial elements of interest. + """ + ) + return + + +@app.cell +def _( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + spatialdata_filepath, +): + vc = VitessceConfig(schema_version='1.0.18', name='SpatialData Demo') + _wrapper = SpatialDataWrapper(sdata_url=spatialdata_filepath, table_path='tables/table', image_path='images/raw_image', obs_segmentations_path='labels/segmentation_mask', obs_feature_matrix_path='tables/table/X', obs_set_paths=['tables/table/obs/annotation'], obs_set_names=['Annotation'], region='nucleus_boundaries', coordinate_system='global', coordination_values={'obsType': 'cell'}) + _dataset = vc.add_dataset(name='Mouse Liver').add_object(_wrapper) + _spatial = vc.add_view('spatialBeta', dataset=_dataset) + feature_list = vc.add_view('featureList', dataset=_dataset) + _layer_controller = vc.add_view('layerControllerBeta', dataset=_dataset) + obs_sets = vc.add_view('obsSets', dataset=_dataset) + _heatmap = vc.add_view('heatmap', dataset=_dataset) + [obs_color_encoding_scope] = vc.add_coordination('obsColorEncoding') + obs_color_encoding_scope.set_value('cellSetSelection') + vc.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image')) + vc.link_views_by_dict([_spatial, _layer_controller], {'segmentationLayer': CL([{'segmentationChannel': CL([{'obsColorEncoding': obs_color_encoding_scope}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'obsSegmentations')) + vc.link_views([_spatial, _layer_controller, feature_list, obs_sets, _heatmap], ['obsType'], [_wrapper.obs_type_label]) + vc.link_views_by_dict([feature_list, obs_sets, _heatmap], {'obsColorEncoding': obs_color_encoding_scope}, meta=False) + vc.layout(_spatial / _heatmap | _layer_controller / (feature_list | obs_sets)) + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### Render the widget""") + return + + +@app.cell +def _(vc): + _vw = vc.widget(height=900) + _vw + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Extract AnnData object from SpatialData object""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + The above example demonstrates how to visualize a spatial 'omics dataset containing not only single-cell information (e.g., a cell-by-gene expression matrix, cell type annotations) but also an image and cell segmentations. + To demonstrate how to use Vitessce to visualize data from a (non-spatial) single-cell experiment, we will extract this information from the SpatialData object and save it to a simpler [AnnData](https://anndata.readthedocs.io/) object (ignoring the imaging and spatially-resolved elements). + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + As Zarr-formatted data can be easily visualized by Vitessce, we recommend saving the AnnData object to a Zarr store using the [write_zarr](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_zarr.html) method. + Optionally, the shape of array chunks (for the AnnData `X` array) can be specified as a parameter, to optimize performance based on data access patterns. + For example, a common pattern is to visualize data across all cells for one gene. + To support such a pattern, the chunk shape can be specified as follows, `(total number of cells, small number of genes)`, resulting in tall-and-skinny array chunks. + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Alternatively, your AnnData object may already be stored using the H5AD (HDF5-based) format. + To demonstrate this scenario, we save the object using the [write_h5ad](https://anndata.readthedocs.io/en/stable/generated/anndata.AnnData.write_h5ad.html) method. + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""To read H5AD-formatted data, Vitessce requires an accompanying JSON [references specification](https://fsspec.github.io/kerchunk/spec.html) file, which can be constructed using the `generate_h5ad_ref_spec` utility function.""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Visualization of an AnnData object""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### Zarr-based AnnData""") + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, adata_zarr_filepath, vt): + vc_1 = VitessceConfig(schema_version='1.0.18', name='AnnData (zarr)') + _wrapper = AnnDataWrapper(adata_url=adata_zarr_filepath, obs_feature_matrix_path='X', obs_set_paths=['obs/annotation'], obs_set_names=['Annotation'], coordination_values={'obsType': 'cell'}) + _dataset = vc_1.add_dataset(name='Mouse Liver').add_object(_wrapper) + _heatmap = vc_1.add_view(vt.HEATMAP, dataset=_dataset) + feature_list_1 = vc_1.add_view(vt.FEATURE_LIST, dataset=_dataset) + obs_sets_1 = vc_1.add_view(vt.OBS_SETS, dataset=_dataset) + _violin_plots = vc_1.add_view('obsSetFeatureValueDistribution', dataset=_dataset) + vc_1.link_views([_heatmap, feature_list_1, obs_sets_1], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]]) + vc_1.layout(_heatmap / _violin_plots | feature_list_1 / obs_sets_1) + return (vc_1,) + + +@app.cell +def _(vc_1): + vc_1.widget() + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### H5AD-based AnnData""") + return + + +@app.cell +def _( + AnnDataWrapper, + VitessceConfig, + adata_h5ad_filepath, + ref_spec_json_filepath, + vt, +): + vc_2 = VitessceConfig(schema_version='1.0.18', name='AnnData (h5ad)') + _wrapper = AnnDataWrapper(adata_url=adata_h5ad_filepath, ref_url=ref_spec_json_filepath, obs_feature_matrix_path='X', obs_set_paths=['obs/annotation'], obs_set_names=['Annotation'], coordination_values={'obsType': 'cell'}) + _dataset = vc_2.add_dataset(name='Mouse Liver').add_object(_wrapper) + _heatmap = vc_2.add_view(vt.HEATMAP, dataset=_dataset) + feature_list_2 = vc_2.add_view(vt.FEATURE_LIST, dataset=_dataset) + obs_sets_2 = vc_2.add_view(vt.OBS_SETS, dataset=_dataset) + _violin_plots = vc_2.add_view('obsSetFeatureValueDistribution', dataset=_dataset) + vc_2.link_views([_heatmap, feature_list_2, obs_sets_2], ['obsType', 'featureValueColormapRange'], ['cell', [0, 0.01]]) + vc_2.layout(_heatmap / _violin_plots | feature_list_2 / obs_sets_2) + return feature_list_2, obs_sets_2, vc_2 + + +@app.cell +def _(vc_2): + vc_2.widget() + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Visualization of an image file""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### OME-Zarr image""") + return + + +@app.cell +def _( + CL, + ImageOmeZarrWrapper, + ObsSegmentationsOmeZarrWrapper, + VitessceConfig, + feature_list_2, + get_initial_coordination_scope_prefix, + hconcat, + labels_ome_zarr_filepath, + obs_sets_2, + ome_zarr_filepath, +): + vc_3 = VitessceConfig(schema_version='1.0.18', name='Image (ome-zarr)') + img_wrapper = ImageOmeZarrWrapper(img_url=ome_zarr_filepath, coordination_values={'fileUid': 'image'}) + segmentations_wrapper = ObsSegmentationsOmeZarrWrapper(img_url=labels_ome_zarr_filepath, coordination_values={'fileUid': 'segmentations'}) + _dataset = vc_3.add_dataset(name='Mouse Liver').add_object(img_wrapper).add_object(segmentations_wrapper) + _spatial = vc_3.add_view('spatialBeta', dataset=_dataset) + _layer_controller = vc_3.add_view('layerControllerBeta', dataset=_dataset) + vc_3.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'fileUid': 'image', 'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image')) + vc_3.link_views_by_dict([_spatial, _layer_controller], {'segmentationLayer': CL([{'fileUid': 'segmentations', 'segmentationChannel': CL([{'obsColorEncoding': 'spatialChannelColor', 'spatialChannelColor': [0, 255, 0], 'spatialChannelOpacity': 0.75, 'spatialSegmentationFilled': False, 'spatialSegmentationStrokeWidth': 0.25}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'obsSegmentations')) + vc_3.link_views([_spatial, _layer_controller, feature_list_2, obs_sets_2], ['obsType'], ['cell']) + vc_3.layout(hconcat(_spatial, _layer_controller, split=(2, 1))) + return (vc_3,) + + +@app.cell +def _(vc_3): + _vw = vc_3.widget() + _vw + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### OME-TIFF image""") + return + + +@app.cell +def _( + CL, + ImageOmeTiffWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + hconcat, + offsets_json_filepath, + ome_tiff_filepath, +): + vc_4 = VitessceConfig(schema_version='1.0.18', name='Image and segmentations (ome-tiff)') + _wrapper = ImageOmeTiffWrapper(img_url=ome_tiff_filepath, offsets_url=offsets_json_filepath) + _dataset = vc_4.add_dataset(name='Mouse Liver').add_object(_wrapper) + _spatial = vc_4.add_view('spatialBeta', dataset=_dataset) + _layer_controller = vc_4.add_view('layerControllerBeta', dataset=_dataset) + vc_4.link_views_by_dict([_spatial, _layer_controller], {'imageLayer': CL([{'photometricInterpretation': 'BlackIsZero', 'imageChannel': CL([{'spatialTargetC': 0, 'spatialChannelColor': [255, 255, 255], 'spatialChannelWindow': [0, 4000]}])}])}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image')) + vc_4.layout(hconcat(_spatial, _layer_controller, split=(2, 1))) + return (vc_4,) + + +@app.cell +def _(vc_4): + vc_4.widget() + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Data location options + + Vitessce can visualize data [not only stored](https://python-docs.vitessce.io/data_options.html) locally (referenced using local file or directory paths) but also stored remotely (referenced using absolute URL paths). + Depending on whether the Python kernel running the Jupyter process is running locally versus remotely (e.g., on a cluster or cloud platform such as Google Colab), certain data locations may be challenging to access from the machine running the Jupyter notebook frontend (i.e., the machine on which the web browser used to view the notebook is installed). + + To provide data via one of these alternative mechanisms, use parameters with the following suffices when instantiating the data wrapper classes. + + - `_path`: Local file or directory + - `_url`: Remote file or directory + - `_store`: Zarr-store-accessible (for zarr-based formats) + - `_artifact`: Lamin artifact + + For example, `adata_path` can be exchanged for one of the following options. + + ```diff + AnnDataWrapper( + - adata_path="./mouse_liver.spatialdata.zarr", + + adata_url="https://example.com/mouse_liver.spatialdata.zarr", # Absolute URL + + adata_store="./mouse_liver.spatialdata.zarr", # String interpreted as root of DirectoryStore + + adata_store=zarr.DirectoryStore("./mouse_liver.spatialdata.zarr"), # Instance of zarr.storage + + adata_artifact=adata_zarr_artifact, # Instance of ln.Artifact + ... + ``` + + Note that the `_store` options are only available for Zarr-based formats, such as AnnDataWrapper, SpatialDataWrapper, and ImageOmeZarrWrapper. + Further, when multiple files are required, such as both `adata_path` and `ref_path` (for the JSON reference specification file accompanying an H5AD file) or `img_path` and `offsets_path`, multiple parameter suffices may need to be changed. + """ + ) + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/spatial_data_visium_hd.ipynb b/docs/notebooks/spatial_data_visium_hd.ipynb deleted file mode 100644 index 0ca3257e..00000000 --- a/docs/notebooks/spatial_data_visium_hd.ipynb +++ /dev/null @@ -1,247 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of a SpatialData object" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Import dependencies\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "import zipfile\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - " SpatialDataWrapper,\n", - " get_initial_coordination_scope_prefix\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "data_dir = \"data\"\n", - "zip_filepath = join(data_dir, \"visium_hd_3.0.0_io.zip\")\n", - "spatialdata_filepath = join(data_dir, \"visium_hd_3.0.0.spatialdata.zarr\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "if not isdir(spatialdata_filepath):\n", - " if not isfile(zip_filepath):\n", - " os.makedirs(data_dir, exist_ok=True)\n", - " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/visium_hd_3.0.0_io.zip', zip_filepath)\n", - " with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n", - " zip_ref.extractall(data_dir)\n", - " os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rasterize bins\n", - "Reference: https://spatialdata.scverse.org/en/stable/tutorials/notebooks/notebooks/examples/technology_visium_hd.html#performant-on-the-fly-data-rasterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from spatialdata import (\n", - " read_zarr,\n", - " rasterize_bins,\n", - " rasterize_bins_link_table_to_labels\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sdata = read_zarr(spatialdata_filepath)\n", - "sdata" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for bin_size in [\"016\", \"008\", \"002\"]:\n", - " # rasterize_bins() requires a compresed sparse column (csc) matrix\n", - " sdata.tables[f\"square_{bin_size}um\"].X = sdata.tables[f\"square_{bin_size}um\"].X.tocsc()\n", - " rasterized = rasterize_bins(\n", - " sdata,\n", - " f\"Visium_HD_Mouse_Small_Intestine_square_{bin_size}um\",\n", - " f\"square_{bin_size}um\",\n", - " \"array_col\",\n", - " \"array_row\",\n", - " # We want to rasterize to a Labels element, rather than an Image element.\n", - " return_region_as_labels=True\n", - " )\n", - " sdata[f\"rasterized_{bin_size}um\"] = rasterized\n", - " rasterize_bins_link_table_to_labels(\n", - " sdata,\n", - " table_name=f\"square_{bin_size}um\",\n", - " rasterized_labels_name=f\"rasterized_{bin_size}um\",\n", - " )\n", - " sdata.write_element(f\"rasterized_{bin_size}um\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sdata" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Vitessce\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(\n", - " schema_version=\"1.0.18\",\n", - " name='Visium HD SpatialData Demo',\n", - ")\n", - "# Add data to the configuration:\n", - "wrapper = SpatialDataWrapper(\n", - " sdata_path=spatialdata_filepath,\n", - " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", - " image_path=\"images/Visium_HD_Mouse_Small_Intestine_full_image\",\n", - " table_path=\"tables/square_016um\",\n", - " obs_feature_matrix_path=\"tables/square_016um/X\",\n", - " obs_segmentations_path=\"labels/rasterized_016um\",\n", - " #region=\"CytAssist_FFPE_Human_Breast_Cancer\",\n", - " coordinate_system=\"Visium_HD_Mouse_Small_Intestine\",\n", - " coordination_values={\n", - " # The following tells Vitessce to consider each observation as a \"bin\"\n", - " \"obsType\": \"bin\",\n", - " }\n", - ")\n", - "dataset = vc.add_dataset(name='Visium HD').add_object(wrapper)\n", - "\n", - "# Add views (visualizations) to the configuration:\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "feature_list = vc.add_view(\"featureList\", dataset=dataset)\n", - "layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'imageLayer': CL([{\n", - " 'photometricInterpretation': 'RGB',\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "vc.link_views_by_dict([spatial, layer_controller], {\n", - " 'segmentationLayer': CL([{\n", - " 'segmentationChannel': CL([{\n", - " 'spatialChannelOpacity': 0.5,\n", - " 'obsColorEncoding': 'geneSelection',\n", - " 'featureValueColormapRange': [0, 0.5],\n", - " }])\n", - " }]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", - "obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", - "vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType', 'featureSelection'], [wrapper.obs_type_label, ['AA986860']])\n", - "\n", - "# Layout the views\n", - "vc.layout(spatial | (feature_list / layer_controller / obs_sets));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Render the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/spatial_data_visium_hd.mo.py b/docs/notebooks/spatial_data_visium_hd.mo.py new file mode 100644 index 00000000..cff90aef --- /dev/null +++ b/docs/notebooks/spatial_data_visium_hd.mo.py @@ -0,0 +1,290 @@ +import marimo + +__generated_with = "0.14.16" +app = marimo.App(width="full") + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""# Vitessce Widget Tutorial""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""# Visualization of a SpatialData object""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Import dependencies""") + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + import zipfile + + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + SpatialDataWrapper, + get_initial_coordination_scope_prefix + ) + return ( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + isdir, + isfile, + join, + os, + urlretrieve, + vt, + zipfile, + ) + + +@app.cell +def _(): + import scanpy as sc + return (sc,) + + +@app.cell +def _(join): + data_dir = "data" + zip_filepath = join(data_dir, "visium_hd_3.0.0_io.zip") + spatialdata_filepath = join(data_dir, "visium_hd_3.0.0.spatialdata.zarr") + return data_dir, spatialdata_filepath, zip_filepath + + +@app.cell +def _( + data_dir, + isdir, + isfile, + join, + os, + spatialdata_filepath, + urlretrieve, + zip_filepath, + zipfile, +): + if not isdir(spatialdata_filepath): + if not isfile(zip_filepath): + os.makedirs(data_dir, exist_ok=True) + urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/visium_hd_3.0.0_io.zip', zip_filepath) + with zipfile.ZipFile(zip_filepath,"r") as zip_ref: + zip_ref.extractall(data_dir) + os.rename(join(data_dir, "data.zarr"), spatialdata_filepath) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Rasterize bins + Reference: https://spatialdata.scverse.org/en/stable/tutorials/notebooks/notebooks/examples/technology_visium_hd.html#performant-on-the-fly-data-rasterization + """ + ) + return + + +@app.cell +def _(): + from spatialdata import ( + read_zarr, + rasterize_bins, + rasterize_bins_link_table_to_labels + ) + return rasterize_bins, rasterize_bins_link_table_to_labels, read_zarr + + +@app.cell +def _( + rasterize_bins, + rasterize_bins_link_table_to_labels, + read_zarr, + spatialdata_filepath, +): + sdata = read_zarr(spatialdata_filepath) + + for bin_size in ["016", "008", "002"]: + # rasterize_bins() requires a compresed sparse column (csc) matrix + sdata.tables[f"square_{bin_size}um"].X = sdata.tables[f"square_{bin_size}um"].X.tocsc() + rasterized = rasterize_bins( + sdata, + f"Visium_HD_Mouse_Small_Intestine_square_{bin_size}um", + f"square_{bin_size}um", + "array_col", + "array_row", + # We want to rasterize to a Labels element, rather than an Image element. + return_region_as_labels=True + ) + sdata[f"rasterized_{bin_size}um"] = rasterized + rasterize_bins_link_table_to_labels( + sdata, + table_name=f"square_{bin_size}um", + rasterized_labels_name=f"rasterized_{bin_size}um", + ) + try: + sdata.write_element(f"rasterized_{bin_size}um") + except: + pass + + sdata + return (sdata,) + + +@app.cell +def _(sdata): + sdata + return + + +@app.cell +def _(mo): + mo.md(r"""## Compute a UMAP embedding of the bins in `square_016um`""") + return + + +@app.cell +def _(sdata): + sdata.tables['square_016um'] + return + + +@app.cell +def _(sc, sdata): + sc.pp.neighbors(sdata.tables['square_016um']) + sc.tl.umap(sdata.tables['square_016um']) + sc.pl.umap(sdata.tables['square_016um']) + return + + +@app.cell +def _(sdata): + sdata.tables['square_016um'] + return + + +@app.function +def write_element(sdata, name): + # Reference: https://github.com/scverse/spatialdata/blob/7604a3d2325079293ff523c5dff4483dffc890cb/tests/io/test_readwrite.py#L216C21-L224C61 + new_name = f"{name}_new_place" + # a. write a backup copy of the data + sdata[new_name] = sdata[name] + sdata.write_element(new_name) + # b. rewrite the original data + sdata.delete_element_from_disk(name) + sdata.write_element(name) + # c. remove the backup copy + del sdata[new_name] + sdata.delete_element_from_disk(new_name) + + +@app.cell +def _(sdata): + write_element(sdata, "square_016um") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Configure Vitessce + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + """ + ) + return + + +@app.cell +def _( + CL, + SpatialDataWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + spatialdata_filepath, + vt, +): + vc = VitessceConfig( + schema_version="1.0.18", + name='Visium HD SpatialData Demo', + ) + # Add data to the configuration: + wrapper = SpatialDataWrapper( + sdata_path=spatialdata_filepath, + # The following paths are relative to the root of the SpatialData zarr store on-disk. + image_path="images/Visium_HD_Mouse_Small_Intestine_full_image", + table_path="tables/square_016um", + obs_feature_matrix_path="tables/square_016um/X", + obs_segmentations_path="labels/rasterized_016um", + #region="CytAssist_FFPE_Human_Breast_Cancer", + coordinate_system="Visium_HD_Mouse_Small_Intestine", + coordination_values={ + # The following tells Vitessce to consider each observation as a "bin" + "obsType": "bin", + } + ) + dataset = vc.add_dataset(name='Visium HD').add_object(wrapper) + + # Add views (visualizations) to the configuration: + spatial = vc.add_view("spatialBeta", dataset=dataset) + feature_list = vc.add_view("featureList", dataset=dataset) + layer_controller = vc.add_view("layerControllerBeta", dataset=dataset) + vc.link_views_by_dict([spatial, layer_controller], { + 'imageLayer': CL([{ + 'photometricInterpretation': 'RGB', + }]), + }, scope_prefix=get_initial_coordination_scope_prefix("A", "image")) + vc.link_views_by_dict([spatial, layer_controller], { + 'segmentationLayer': CL([{ + 'segmentationChannel': CL([{ + 'spatialChannelOpacity': 0.5, + 'obsColorEncoding': 'geneSelection', + 'featureValueColormapRange': [0, 0.5], + 'obsHighlight': None + }]) + }]), + }, scope_prefix=get_initial_coordination_scope_prefix("A", "obsSegmentations")) + obs_sets = vc.add_view(vt.OBS_SETS, dataset=dataset) + vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType', 'featureSelection'], [wrapper.obs_type_label, ['AA986860']]) + + # Layout the views + vc.layout(spatial | (feature_list / layer_controller / obs_sets)); + return (vc,) + + +@app.cell +def _(mo): + mo.md(r"""### Render the widget""") + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/web_app_brain.ipynb b/docs/notebooks/web_app_brain.ipynb deleted file mode 100644 index bd3d6eb0..00000000 --- a/docs/notebooks/web_app_brain.ipynb +++ /dev/null @@ -1,253 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Web App Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of single-cell RNA seq data using vitessce.io" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Download the data\n", - "\n", - "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "os.makedirs(\"data\", exist_ok=True)\n", - "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", - "urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Load the data\n", - "\n", - "Note: this function may print a `FutureWarning`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = read_h5ad(adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.1. Preprocess the Data For Visualization\n", - "\n", - "This dataset contains 25,587 genes. In order to visualize it efficiently, we convert it to CSC sparse format so that we can make fast requests for gene data. We also prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "top_dispersion = adata.var[\"dispersions_norm\"][\n", - " sorted(\n", - " range(len(adata.var[\"dispersions_norm\"])),\n", - " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", - " )[-51:][0]\n", - "]\n", - "adata.var[\"top_highly_variable\"] = (\n", - " adata.var[\"dispersions_norm\"] > top_dispersion\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Create the Vitessce widget configuration\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.1. Instantiate a `VitessceConfig` object\n", - "\n", - "Use the `VitessceConfig(name, description)` constructor to create an instance." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', description='COVID-19 Healthy Donor Brain')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.2. Add a dataset to the `VitessceConfig` instance\n", - "\n", - "In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance.\n", - "\n", - "Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a \"data wrapper\" instance to our new dataset. For example, the `AnnDataWrapper` class knows how to convert AnnData objects to the corresponding Vitessce data types.\n", - "\n", - "Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `cell_set_obs_cols` to tell Vitessce which columns of the `obs` dataframe correspond to cell sets." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", - " adata,\n", - " obs_embedding_paths=[\"obsm/X_umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/CellType\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_feature_matrix_path=\"X\",\n", - " feature_filter_path=\"var/top_highly_variable\"\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.3. Add visualizations to the `VitessceConfig` instance\n", - "\n", - "Now that we have added a dataset, we can configure visualizations. The `.add_view(dataset, component_type)` method adds a view (i.e. visualization or controller component) to the configuration.\n", - "\n", - "The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter.\n", - "\n", - "For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.4. Define the visualization layout\n", - "\n", - "The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.layout((scatterplot | cell_sets) / (heatmap | genes));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Launch the web application\n", - "\n", - "The `vc.web_app()` method serves the processed data locally and opens a web browser to `http://vitessce.io/?url={config_as_json}`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.web_app()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/web_app_brain.mo.py b/docs/notebooks/web_app_brain.mo.py new file mode 100644 index 00000000..13860962 --- /dev/null +++ b/docs/notebooks/web_app_brain.mo.py @@ -0,0 +1,249 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Vitessce Web App Tutorial + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of single-cell RNA seq data using vitessce.io + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + return AnnDataWrapper, VitessceConfig, cm, join, os, read_h5ad, urlretrieve + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Download the data + + For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17. + """ + ) + return + + +@app.cell +def _(join, os, urlretrieve): + os.makedirs("data", exist_ok=True) + adata_filepath = join("data", "habib17.processed.h5ad") + urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath) + return (adata_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Load the data + + Note: this function may print a `FutureWarning` + """ + ) + return + + +@app.cell +def _(adata_filepath, read_h5ad): + adata = read_h5ad(adata_filepath) + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3.1. Preprocess the Data For Visualization + + This dataset contains 25,587 genes. In order to visualize it efficiently, we convert it to CSC sparse format so that we can make fast requests for gene data. We also prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap. + """ + ) + return + + +@app.cell +def _(adata): + top_dispersion = adata.var["dispersions_norm"][ + sorted( + range(len(adata.var["dispersions_norm"])), + key=lambda k: adata.var["dispersions_norm"][k], + )[-51:][0] + ] + adata.var["top_highly_variable"] = ( + adata.var["dispersions_norm"] > top_dispersion + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Create the Vitessce widget configuration + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 4.1. Instantiate a `VitessceConfig` object + + Use the `VitessceConfig(name, description)` constructor to create an instance. + """ + ) + return + + +@app.cell +def _(VitessceConfig): + vc = VitessceConfig(schema_version="1.0.15", name='Habib et al', description='COVID-19 Healthy Donor Brain') + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 4.2. Add a dataset to the `VitessceConfig` instance + + In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance. + + Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a "data wrapper" instance to our new dataset. For example, the `AnnDataWrapper` class knows how to convert AnnData objects to the corresponding Vitessce data types. + + Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `cell_set_obs_cols` to tell Vitessce which columns of the `obs` dataframe correspond to cell sets. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, adata, vc): + dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper( + adata, + obs_embedding_paths=["obsm/X_umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/CellType"], + obs_set_names=["Cell Type"], + obs_feature_matrix_path="X", + feature_filter_path="var/top_highly_variable" + ) + ) + return (dataset,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 4.3. Add visualizations to the `VitessceConfig` instance + + Now that we have added a dataset, we can configure visualizations. The `.add_view(dataset, component_type)` method adds a view (i.e. visualization or controller component) to the configuration. + + The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter. + + For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot. + """ + ) + return + + +@app.cell +def _(cm, dataset, vc): + scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + heatmap = vc.add_view(cm.HEATMAP, dataset=dataset) + return cell_sets, genes, heatmap, scatterplot + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 4.4. Define the visualization layout + + The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively. + """ + ) + return + + +@app.cell +def _(cell_sets, genes, heatmap, scatterplot, vc): + vc.layout((scatterplot | cell_sets) / (heatmap | genes)); + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Launch the web application + + The `vc.web_app()` method serves the processed data locally and opens a web browser to `http://vitessce.io/?url={config_as_json}` + """ + ) + return + + +@app.cell +def _(vc): + vc.web_app() + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_anndata_with_image.ipynb b/docs/notebooks/widget_anndata_with_image.ipynb deleted file mode 100644 index f76d87f9..00000000 --- a/docs/notebooks/widget_anndata_with_image.ipynb +++ /dev/null @@ -1,130 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of AnnData object containing an image in `uns`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note: This approach to storing images within AnnData objects is no longer recommended now that [SpatialData](https://spatialdata.scverse.org/en/stable/) has been introduced." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import scanpy as sc\n", - "import numpy as np\n", - "from vitessce.data_utils import rgb_img_to_ome_zarr, VAR_CHUNK_SIZE\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " AnnDataWrapper,\n", - " ImageOmeZarrWrapper,\n", - ")\n", - "from os.path import join" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "output_img = join(\"data\", \"V1_Human_Lymph_Node.ome.zarr\")\n", - "output_adata = join(\"data\", \"V1_Human_Lymph_Node.anndata.zarr\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = sc.datasets.visium_sge(sample_id=\"V1_Human_Lymph_Node\", include_hires_tiff=True)\n", - "\n", - "# Write img_arr to OME-Zarr.\n", - "# Need to convert images from interleaved to non-interleaved (color axis should be first).\n", - "img_hires = adata.uns['spatial']['V1_Human_Lymph_Node']['images']['hires']\n", - "img_arr = np.transpose(img_hires, (2, 0, 1))\n", - "# Convert values from [0, 1] to [0, 255].\n", - "img_arr *= 255.0\n", - "\n", - "# First, save the image to an OME-Zarr image format\n", - "rgb_img_to_ome_zarr(img_arr, output_img, axes=\"cyx\", chunks=(1, 256, 256), img_name=\"Image\")\n", - "# Second, save the AnnData object to Zarr format\n", - "adata.write_zarr(output_adata, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.17\", name=\"AnnData with image\")\n", - "dataset = vc.add_dataset(\"My dataset\").add_object(\n", - " AnnDataWrapper(\n", - " adata_path=output_adata,\n", - " \n", - " )\n", - ").add_object(\n", - " ImageOmeZarrWrapper(\n", - " img_path=output_img,\n", - " )\n", - ")\n", - "\n", - "spatial_view = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "lc_view = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.layout(spatial_view | lc_view);" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_anndata_with_image.mo.py b/docs/notebooks/widget_anndata_with_image.mo.py new file mode 100644 index 00000000..2e8a042e --- /dev/null +++ b/docs/notebooks/widget_anndata_with_image.mo.py @@ -0,0 +1,116 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of AnnData object containing an image in `uns` + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Note: This approach to storing images within AnnData objects is no longer recommended now that [SpatialData](https://spatialdata.scverse.org/en/stable/) has been introduced. + """ + ) + return + + +@app.cell +def _(): + import scanpy as sc + import numpy as np + from vitessce.data_utils import rgb_img_to_ome_zarr, VAR_CHUNK_SIZE + from vitessce import ( + VitessceConfig, + AnnDataWrapper, + ImageOmeZarrWrapper, + ) + from os.path import join + return ( + AnnDataWrapper, + ImageOmeZarrWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + join, + np, + rgb_img_to_ome_zarr, + sc, + ) + + +@app.cell +def _(join): + output_img = join("data", "V1_Human_Lymph_Node.ome.zarr") + output_adata = join("data", "V1_Human_Lymph_Node.anndata.zarr") + return output_adata, output_img + + +@app.cell +def _(VAR_CHUNK_SIZE, np, output_adata, output_img, rgb_img_to_ome_zarr, sc): + adata = sc.datasets.visium_sge(sample_id="V1_Human_Lymph_Node", include_hires_tiff=True) + + # Write img_arr to OME-Zarr. + # Need to convert images from interleaved to non-interleaved (color axis should be first). + img_hires = adata.uns['spatial']['V1_Human_Lymph_Node']['images']['hires'] + img_arr = np.transpose(img_hires, (2, 0, 1)) + # Convert values from [0, 1] to [0, 255]. + img_arr *= 255.0 + + # First, save the image to an OME-Zarr image format + rgb_img_to_ome_zarr(img_arr, output_img, axes="cyx", chunks=(1, 256, 256), img_name="Image") + # Second, save the AnnData object to Zarr format + adata.write_zarr(output_adata, chunks=[adata.shape[0], VAR_CHUNK_SIZE]) + return + + +@app.cell +def _( + AnnDataWrapper, + ImageOmeZarrWrapper, + VitessceConfig, + output_adata, + output_img, +): + vc = VitessceConfig(schema_version="1.0.17", name="AnnData with image") + dataset = vc.add_dataset("My dataset").add_object( + AnnDataWrapper( + adata_path=output_adata, + + ) + ).add_object( + ImageOmeZarrWrapper( + img_path=output_img, + ) + ) + + spatial_view = vc.add_view("spatialBeta", dataset=dataset) + lc_view = vc.add_view("layerControllerBeta", dataset=dataset) + + vc.layout(spatial_view | lc_view); + return (vc,) + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_brain.ipynb b/docs/notebooks/widget_brain.ipynb deleted file mode 100644 index 759bd928..00000000 --- a/docs/notebooks/widget_brain.ipynb +++ /dev/null @@ -1,295 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of single-cell RNA seq data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")\n", - "from vitessce.data_utils import (\n", - " optimize_adata,\n", - " VAR_CHUNK_SIZE,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Download the data\n", - "\n", - "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", - "if not isfile(adata_filepath):\n", - " os.makedirs(\"data\", exist_ok=True)\n", - " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Load the data\n", - "\n", - "Note: this function may print a `FutureWarning`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = read_h5ad(adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## 3.1. Preprocess the Data For Visualization\n", - "\n", - "This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "top_dispersion = adata.var[\"dispersions_norm\"][\n", - " sorted(\n", - " range(len(adata.var[\"dispersions_norm\"])),\n", - " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", - " )[-51:][0]\n", - "]\n", - "adata.var[\"top_highly_variable\"] = (\n", - " adata.var[\"dispersions_norm\"] > top_dispersion\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.2 Save the Data to Zarr store\n", - "\n", - "We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zarr_filepath = join(\"data\", \"habib17.processed.zarr\")\n", - "if not isdir(zarr_filepath):\n", - " adata = optimize_adata(\n", - " adata,\n", - " obs_cols=[\"CellType\"],\n", - " obsm_keys=[\"X_umap\"],\n", - " optimize_X=True,\n", - " var_cols=[\"top_highly_variable\"],\n", - " )\n", - " adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Create the Vitessce widget configuration\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.1. Instantiate a `VitessceConfig` object\n", - "\n", - "Use the `VitessceConfig` constructor to create an instance." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', description='COVID-19 Healthy Donor Brain')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.2. Add a dataset to the `VitessceConfig` instance\n", - "\n", - "In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance.\n", - "\n", - "Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a \"data wrapper\" instance to our new dataset. For example, the `AnnDataWrapper` helps to configure AnnData Zarr stores for use in the Vitessce configuration.\n", - "\n", - "Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `obs_set_paths` to tell Vitessce that certain columns of the `obs` dataframe correspond to cell type annotations or cell clusterings." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", - " adata_path=zarr_filepath,\n", - " obs_embedding_paths=[\"obsm/X_umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/CellType\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_feature_matrix_path=\"X\",\n", - " initial_feature_filter_path=\"var/top_highly_variable\"\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.3. Add visualizations to the `VitessceConfig` instance\n", - "\n", - "Now that we have added a dataset, we can configure visualizations. The `.add_view` method adds a view (i.e. visualization or controller component) to the configuration.\n", - "\n", - "The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter.\n", - "\n", - "For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.4. Define the visualization layout\n", - "\n", - "The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.layout((scatterplot | cell_sets) / (heatmap | genes));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Create the widget\n", - "\n", - "The `vc.widget()` method returns the configured widget instance." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_brain.mo.py b/docs/notebooks/widget_brain.mo.py new file mode 100644 index 00000000..b1c4b00a --- /dev/null +++ b/docs/notebooks/widget_brain.mo.py @@ -0,0 +1,278 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of single-cell RNA seq data + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + from vitessce.data_utils import ( + optimize_adata, + VAR_CHUNK_SIZE, + ) + return ( + AnnDataWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + cm, + isdir, + isfile, + join, + optimize_adata, + os, + read_h5ad, + urlretrieve, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Download the data + + For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17. + """ + ) + return + + +@app.cell +def _(isfile, join, os, urlretrieve): + adata_filepath = join("data", "habib17.processed.h5ad") + if not isfile(adata_filepath): + os.makedirs("data", exist_ok=True) + urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath) + return (adata_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Load the data + + Note: this function may print a `FutureWarning` + """ + ) + return + + +@app.cell +def _(adata_filepath, read_h5ad): + adata = read_h5ad(adata_filepath) + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3.1. Preprocess the Data For Visualization + + This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap. + """ + ) + return + + +@app.cell +def _(adata): + top_dispersion = adata.var["dispersions_norm"][ + sorted( + range(len(adata.var["dispersions_norm"])), + key=lambda k: adata.var["dispersions_norm"][k], + )[-51:][0] + ] + adata.var["top_highly_variable"] = ( + adata.var["dispersions_norm"] > top_dispersion + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3.2 Save the Data to Zarr store + + We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object. + """ + ) + return + + +@app.cell +def _(VAR_CHUNK_SIZE, adata, isdir, join, optimize_adata): + zarr_filepath = join('data', 'habib17.processed.zarr') + if not isdir(zarr_filepath): + adata_1 = optimize_adata(adata, obs_cols=['CellType'], obsm_keys=['X_umap'], optimize_X=True, var_cols=['top_highly_variable']) + adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE]) + return (zarr_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Create the Vitessce widget configuration + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 4.1. Instantiate a `VitessceConfig` object + + Use the `VitessceConfig` constructor to create an instance. + """ + ) + return + + +@app.cell +def _(VitessceConfig): + vc = VitessceConfig(schema_version="1.0.15", name='Habib et al', description='COVID-19 Healthy Donor Brain') + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 4.2. Add a dataset to the `VitessceConfig` instance + + In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance. + + Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a "data wrapper" instance to our new dataset. For example, the `AnnDataWrapper` helps to configure AnnData Zarr stores for use in the Vitessce configuration. + + Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `obs_set_paths` to tell Vitessce that certain columns of the `obs` dataframe correspond to cell type annotations or cell clusterings. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, vc, zarr_filepath): + dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper( + adata_path=zarr_filepath, + obs_embedding_paths=["obsm/X_umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/CellType"], + obs_set_names=["Cell Type"], + obs_feature_matrix_path="X", + initial_feature_filter_path="var/top_highly_variable" + ) + ) + return (dataset,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 4.3. Add visualizations to the `VitessceConfig` instance + + Now that we have added a dataset, we can configure visualizations. The `.add_view` method adds a view (i.e. visualization or controller component) to the configuration. + + The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter. + + For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot. + """ + ) + return + + +@app.cell +def _(cm, dataset, vc): + scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + heatmap = vc.add_view(cm.HEATMAP, dataset=dataset) + return cell_sets, genes, heatmap, scatterplot + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 4.4. Define the visualization layout + + The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively. + """ + ) + return + + +@app.cell +def _(cell_sets, genes, heatmap, scatterplot, vc): + vc.layout((scatterplot | cell_sets) / (heatmap | genes)); + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Create the widget + + The `vc.widget()` method returns the configured widget instance. + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_brain_h5ad.ipynb b/docs/notebooks/widget_brain_h5ad.ipynb deleted file mode 100644 index 66183ceb..00000000 --- a/docs/notebooks/widget_brain_h5ad.ipynb +++ /dev/null @@ -1,170 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of single-cell RNA seq data from H5AD file" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "import json\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")\n", - "from vitessce.data_utils import (\n", - " generate_h5ad_ref_spec\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 0. Download data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "h5_url = \"https://datasets.cellxgene.cziscience.com/84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata_filepath = join(\"data\", \"84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad\")\n", - "if not isfile(adata_filepath):\n", - " os.makedirs(\"data\", exist_ok=True)\n", - " urlretrieve(h5_url, adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Create a Reference Spec JSON file for the H5AD file\n", - "\n", - "In order for Vitessce to load H5AD files, we also need to provide a corresponding [Reference Spec](https://fsspec.github.io/kerchunk/spec.html) JSON file which contains mappings between AnnData object keys and the byte offsets at which those AnnData object values begin within the H5AD file binary contents." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "json_filepath = join(\"data\", \"84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad.reference.json\")\n", - "if not isfile(json_filepath):\n", - " ref_dict = generate_h5ad_ref_spec(h5_url)\n", - " with open(json_filepath, \"w\") as f:\n", - " json.dump(ref_dict, f)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## 2. Create the Vitessce widget configuration\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.17\", name='Nakshatri et al', description='snRNA-seq analyses of breast tissues of healthy women of diverse genetic ancestry')\n", - "\n", - "dataset = vc.add_dataset(name='84df8fa1').add_object(AnnDataWrapper(\n", - " adata_path=adata_filepath,\n", - " ref_path=json_filepath, # We specify paths to both the H5AD and JSON files\n", - " obs_embedding_paths=[\"obsm/X_wnn.umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/cell_type\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_feature_matrix_path=\"X\",\n", - " )\n", - ")\n", - "\n", - "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "cell_set_sizes = vc.add_view(cm.OBS_SET_SIZES, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "\n", - "vc.layout((scatterplot | cell_sets) / (cell_set_sizes | genes));" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## 3. Create the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_brain_h5ad.mo.py b/docs/notebooks/widget_brain_h5ad.mo.py new file mode 100644 index 00000000..1ca6402b --- /dev/null +++ b/docs/notebooks/widget_brain_h5ad.mo.py @@ -0,0 +1,154 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of single-cell RNA seq data from H5AD file + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + import json + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + from vitessce.data_utils import ( + generate_h5ad_ref_spec + ) + return ( + AnnDataWrapper, + VitessceConfig, + cm, + generate_h5ad_ref_spec, + isfile, + join, + json, + os, + urlretrieve, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 0. Download data + """ + ) + return + + +@app.cell +def _(): + h5_url = "https://datasets.cellxgene.cziscience.com/84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad" + return (h5_url,) + + +@app.cell +def _(h5_url, isfile, join, os, urlretrieve): + adata_filepath = join("data", "84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad") + if not isfile(adata_filepath): + os.makedirs("data", exist_ok=True) + urlretrieve(h5_url, adata_filepath) + return (adata_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Create a Reference Spec JSON file for the H5AD file + + In order for Vitessce to load H5AD files, we also need to provide a corresponding [Reference Spec](https://fsspec.github.io/kerchunk/spec.html) JSON file which contains mappings between AnnData object keys and the byte offsets at which those AnnData object values begin within the H5AD file binary contents. + """ + ) + return + + +@app.cell +def _(generate_h5ad_ref_spec, h5_url, isfile, join, json): + json_filepath = join("data", "84df8fa1-ab53-43c9-a439-95dcb9148265.h5ad.reference.json") + if not isfile(json_filepath): + ref_dict = generate_h5ad_ref_spec(h5_url) + with open(json_filepath, "w") as f: + json.dump(ref_dict, f) + return (json_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Create the Vitessce widget configuration + + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, adata_filepath, cm, json_filepath): + vc = VitessceConfig(schema_version="1.0.17", name='Nakshatri et al', description='snRNA-seq analyses of breast tissues of healthy women of diverse genetic ancestry') + + dataset = vc.add_dataset(name='84df8fa1').add_object(AnnDataWrapper( + adata_path=adata_filepath, + ref_path=json_filepath, # We specify paths to both the H5AD and JSON files + obs_embedding_paths=["obsm/X_wnn.umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/cell_type"], + obs_set_names=["Cell Type"], + obs_feature_matrix_path="X", + ) + ) + + scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + cell_set_sizes = vc.add_view(cm.OBS_SET_SIZES, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + + vc.layout((scatterplot | cell_sets) / (cell_set_sizes | genes)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Create the widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_brain_with_base_dir.ipynb b/docs/notebooks/widget_brain_with_base_dir.ipynb deleted file mode 100644 index 41da8211..00000000 --- a/docs/notebooks/widget_brain_with_base_dir.ipynb +++ /dev/null @@ -1,329 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Configure relative to a base_dir" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - " BASE_URL_PLACEHOLDER,\n", - ")\n", - "from vitessce.data_utils import (\n", - " optimize_adata,\n", - " VAR_CHUNK_SIZE,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Define a `base_dir`\n", - "\n", - "We will define a `base_dir` inside which our data will live. We will provide this to `VitessceConfig` in order to construct a configuration that contains URL paths relative to this directory." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "BASE_DIR = \"data\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Download the data\n", - "\n", - "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata_relative_filepath = \"habib17.processed.h5ad\" # Relative to BASE_DIR\n", - "adata_filepath = join(BASE_DIR, adata_relative_filepath)\n", - "if not isfile(adata_filepath):\n", - " os.makedirs(BASE_DIR, exist_ok=True)\n", - " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Load the data\n", - "\n", - "Note: this function may print a `FutureWarning`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = read_h5ad(adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## 4.1. Preprocess the Data For Visualization\n", - "\n", - "This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "top_dispersion = adata.var[\"dispersions_norm\"][\n", - " sorted(\n", - " range(len(adata.var[\"dispersions_norm\"])),\n", - " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", - " )[-51:][0]\n", - "]\n", - "adata.var[\"top_highly_variable\"] = (\n", - " adata.var[\"dispersions_norm\"] > top_dispersion\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4.2 Save the Data to Zarr store\n", - "\n", - "We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zarr_relative_filepath = \"habib17.processed.zarr\" # Relative to BASE_DIR\n", - "zarr_filepath = join(BASE_DIR, zarr_relative_filepath)\n", - "if not isdir(zarr_filepath):\n", - " adata = optimize_adata(\n", - " adata,\n", - " obs_cols=[\"CellType\"],\n", - " obsm_keys=[\"X_umap\"],\n", - " optimize_X=True,\n", - " var_cols=[\"top_highly_variable\"],\n", - " )\n", - " adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Create the Vitessce widget configuration\n", - "\n", - "Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 5.1. Instantiate a `VitessceConfig` object\n", - "\n", - "Use the `VitessceConfig` constructor to create an instance. In this case, we want to construct our configuration using local data that is relative to a particular directory, so we provide the `base_dir` parameter.\n", - "\n", - "Note: This `base_dir` parameter is optional. When it is omitted, local data paths are assumed to be relative to the current working directory." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Habib et al', base_dir=BASE_DIR)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 5.2. Add a dataset to the `VitessceConfig` instance\n", - "\n", - "In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance.\n", - "\n", - "Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a \"data wrapper\" instance to our new dataset. For example, the `AnnDataWrapper` helps to configure AnnData Zarr stores for use in the Vitessce configuration.\n", - "\n", - "Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `obs_set_paths` to tell Vitessce that certain columns of the `obs` dataframe correspond to cell type annotations or cell clusterings." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", - " adata_path=zarr_relative_filepath, # Relative to BASE_DIR (because we specified base_dir in the VitessceConfig constructor)\n", - " obs_embedding_paths=[\"obsm/X_umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/CellType\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_feature_matrix_path=\"X\",\n", - " initial_feature_filter_path=\"var/top_highly_variable\"\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 5.3. Add visualizations to the `VitessceConfig` instance\n", - "\n", - "Now that we have added a dataset, we can configure visualizations. The `.add_view` method adds a view (i.e. visualization or controller component) to the configuration.\n", - "\n", - "The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter.\n", - "\n", - "For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 5.4. Define the visualization layout\n", - "\n", - "The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc.layout((scatterplot | cell_sets) / (heatmap | genes));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Create the widget\n", - "\n", - "The `vc.widget()` method returns the configured widget instance." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Check the URLs in the configuration\n", - "\n", - "We can check that the data URLs in the configuration respected the specified `base_dir`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "config_dict = vc.to_dict(base_url=BASE_URL_PLACEHOLDER)\n", - "config_dict" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_brain_with_base_dir.mo.py b/docs/notebooks/widget_brain_with_base_dir.mo.py new file mode 100644 index 00000000..e64eab5a --- /dev/null +++ b/docs/notebooks/widget_brain_with_base_dir.mo.py @@ -0,0 +1,320 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Configure relative to a base_dir + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + from anndata import read_h5ad + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + BASE_URL_PLACEHOLDER, + ) + from vitessce.data_utils import ( + optimize_adata, + VAR_CHUNK_SIZE, + ) + return ( + AnnDataWrapper, + BASE_URL_PLACEHOLDER, + VAR_CHUNK_SIZE, + VitessceConfig, + cm, + isdir, + isfile, + join, + optimize_adata, + os, + read_h5ad, + urlretrieve, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Define a `base_dir` + + We will define a `base_dir` inside which our data will live. We will provide this to `VitessceConfig` in order to construct a configuration that contains URL paths relative to this directory. + """ + ) + return + + +@app.cell +def _(): + BASE_DIR = "data" + return (BASE_DIR,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Download the data + + For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17. + """ + ) + return + + +@app.cell +def _(BASE_DIR, isfile, join, os, urlretrieve): + adata_relative_filepath = "habib17.processed.h5ad" # Relative to BASE_DIR + adata_filepath = join(BASE_DIR, adata_relative_filepath) + if not isfile(adata_filepath): + os.makedirs(BASE_DIR, exist_ok=True) + urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath) + return (adata_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Load the data + + Note: this function may print a `FutureWarning` + """ + ) + return + + +@app.cell +def _(adata_filepath, read_h5ad): + adata = read_h5ad(adata_filepath) + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4.1. Preprocess the Data For Visualization + + This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap. + """ + ) + return + + +@app.cell +def _(adata): + top_dispersion = adata.var["dispersions_norm"][ + sorted( + range(len(adata.var["dispersions_norm"])), + key=lambda k: adata.var["dispersions_norm"][k], + )[-51:][0] + ] + adata.var["top_highly_variable"] = ( + adata.var["dispersions_norm"] > top_dispersion + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4.2 Save the Data to Zarr store + + We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object. + """ + ) + return + + +@app.cell +def _(BASE_DIR, VAR_CHUNK_SIZE, adata, isdir, join, optimize_adata): + zarr_relative_filepath = 'habib17.processed.zarr' + zarr_filepath = join(BASE_DIR, zarr_relative_filepath) + if not isdir(zarr_filepath): + adata_1 = optimize_adata(adata, obs_cols=['CellType'], obsm_keys=['X_umap'], optimize_X=True, var_cols=['top_highly_variable']) + adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE]) + return (zarr_relative_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Create the Vitessce widget configuration + + Vitessce needs to know which pieces of data we are interested in visualizing, the visualization types we would like to use, and how we want to coordinate (or link) the views. + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 5.1. Instantiate a `VitessceConfig` object + + Use the `VitessceConfig` constructor to create an instance. In this case, we want to construct our configuration using local data that is relative to a particular directory, so we provide the `base_dir` parameter. + + Note: This `base_dir` parameter is optional. When it is omitted, local data paths are assumed to be relative to the current working directory. + """ + ) + return + + +@app.cell +def _(BASE_DIR, VitessceConfig): + vc = VitessceConfig(schema_version="1.0.15", name='Habib et al', base_dir=BASE_DIR) + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 5.2. Add a dataset to the `VitessceConfig` instance + + In Vitessce, a dataset is a container for one file per data type. The `.add_dataset(name)` method on the `vc` instance sets up and returns a new dataset instance. + + Then, we can call the dataset's `.add_object(wrapper_object)` method to attach a "data wrapper" instance to our new dataset. For example, the `AnnDataWrapper` helps to configure AnnData Zarr stores for use in the Vitessce configuration. + + Dataset wrapper classes may require additional parameters to resolve ambiguities. For instance, `AnnData` objects may store multiple clusterings or cell type annotation columns in the `adata.obs` dataframe. We can use the parameter `obs_set_paths` to tell Vitessce that certain columns of the `obs` dataframe correspond to cell type annotations or cell clusterings. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, vc, zarr_relative_filepath): + dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper( + adata_path=zarr_relative_filepath, # Relative to BASE_DIR (because we specified base_dir in the VitessceConfig constructor) + obs_embedding_paths=["obsm/X_umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/CellType"], + obs_set_names=["Cell Type"], + obs_feature_matrix_path="X", + initial_feature_filter_path="var/top_highly_variable" + ) + ) + return (dataset,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 5.3. Add visualizations to the `VitessceConfig` instance + + Now that we have added a dataset, we can configure visualizations. The `.add_view` method adds a view (i.e. visualization or controller component) to the configuration. + + The `Component` enum class (which we have imported as `cm` here) can be used to fill in the `component_type` parameter. + + For convenience, the `SCATTERPLOT` component type takes the extra `mapping` keyword argument, which specifies which embedding should be used for mapping cells to (x,y) points on the plot. + """ + ) + return + + +@app.cell +def _(cm, dataset, vc): + scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + heatmap = vc.add_view(cm.HEATMAP, dataset=dataset) + return cell_sets, genes, heatmap, scatterplot + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ### 5.4. Define the visualization layout + + The `vc.layout(view_concat)` method allows us to specify how our views will be arranged in the layout grid in the widget. The `|` and `/` characters are magic syntax for `hconcat(v1, v2)` and `vconcat(v1, v2)`, respectively. + """ + ) + return + + +@app.cell +def _(cell_sets, genes, heatmap, scatterplot, vc): + vc.layout((scatterplot | cell_sets) / (heatmap | genes)); + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 6. Create the widget + + The `vc.widget()` method returns the configured widget instance. + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 7. Check the URLs in the configuration + + We can check that the data URLs in the configuration respected the specified `base_dir`. + """ + ) + return + + +@app.cell +def _(BASE_URL_PLACEHOLDER, vc): + config_dict = vc.to_dict(base_url=BASE_URL_PLACEHOLDER) + config_dict + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_brain_with_quality_metric.ipynb b/docs/notebooks/widget_brain_with_quality_metric.ipynb deleted file mode 100644 index c273c72b..00000000 --- a/docs/notebooks/widget_brain_with_quality_metric.ipynb +++ /dev/null @@ -1,251 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of single-cell RNA seq data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")\n", - "from vitessce.data_utils import (\n", - " optimize_adata,\n", - " VAR_CHUNK_SIZE,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Download the data\n", - "\n", - "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", - "if not isfile(adata_filepath):\n", - " os.makedirs(\"data\", exist_ok=True)\n", - " urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Load the data\n", - "\n", - "Note: this function may print a `FutureWarning`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = read_h5ad(adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## 3.1. Preprocess the Data For Visualization\n", - "\n", - "This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "top_dispersion = adata.var[\"dispersions_norm\"][\n", - " sorted(\n", - " range(len(adata.var[\"dispersions_norm\"])),\n", - " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", - " )[-51:][0]\n", - "]\n", - "adata.var[\"top_highly_variable\"] = (\n", - " adata.var[\"dispersions_norm\"] > top_dispersion\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.2 Save the Data to Zarr store\n", - "\n", - "We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zarr_filepath = join(\"data\", \"habib17.h5ad.zarr\")\n", - "if not isdir(zarr_filepath):\n", - " adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(\n", - " schema_version=\"1.0.17\",\n", - " name='Habib et al',\n", - " description='COVID-19 Healthy Donor Brain'\n", - ")\n", - "\n", - "# Add data.\n", - "dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper(\n", - " adata_path=zarr_filepath,\n", - " obs_embedding_paths=[\"obsm/X_umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/CellType\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_feature_matrix_path=\"X\",\n", - " initial_feature_filter_path=\"var/top_highly_variable\",\n", - " coordination_values={\n", - " \"obsType\": 'cell',\n", - " \"featureType\": 'gene',\n", - " \"featureValueType\": 'expression',\n", - " },\n", - ")).add_object(AnnDataWrapper(\n", - " adata_path=zarr_filepath,\n", - " obs_feature_column_paths=[\"obs/percent_mito\"],\n", - " coordination_values={\n", - " \"obsType\": 'cell',\n", - " \"featureType\": 'qualityMetric',\n", - " \"featureValueType\": 'value',\n", - " }\n", - "))\n", - "\n", - "# Add views.\n", - "scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "scatterplot_2 = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "histogram = vc.add_view(cm.FEATURE_VALUE_HISTOGRAM, dataset=dataset)\n", - "\n", - "# Link views.\n", - "\n", - "# Color one of the two scatterplots by the percent_mito quality metric.\n", - "# Also use this quality metric for the histogram values.\n", - "vc.link_views_by_dict([histogram, scatterplot_2], {\n", - " \"obsType\": 'cell',\n", - " \"featureType\": 'qualityMetric',\n", - " \"featureValueType\": 'value',\n", - " \"featureSelection\": [\"percent_mito\"],\n", - " \"obsColorEncoding\": \"geneSelection\",\n", - "}, meta=False)\n", - "\n", - "# Synchronize the zooming and panning of the two scatterplots\n", - "vc.link_views_by_dict([scatterplot, scatterplot_2], {\n", - " \"embeddingZoom\": None,\n", - " \"embeddingTargetX\": None,\n", - " \"embeddingTargetY\": None,\n", - "}, meta=False)\n", - "\n", - "# Define the layout.\n", - "vc.layout((scatterplot | (cell_sets / genes)) / (scatterplot_2 | histogram));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Create the widget\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_brain_with_quality_metric.mo.py b/docs/notebooks/widget_brain_with_quality_metric.mo.py new file mode 100644 index 00000000..6ea720a8 --- /dev/null +++ b/docs/notebooks/widget_brain_with_quality_metric.mo.py @@ -0,0 +1,234 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of single-cell RNA seq data + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + from vitessce.data_utils import ( + optimize_adata, + VAR_CHUNK_SIZE, + ) + return ( + AnnDataWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + cm, + isdir, + isfile, + join, + os, + read_h5ad, + urlretrieve, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Download the data + + For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17. + """ + ) + return + + +@app.cell +def _(isfile, join, os, urlretrieve): + adata_filepath = join("data", "habib17.processed.h5ad") + if not isfile(adata_filepath): + os.makedirs("data", exist_ok=True) + urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath) + return (adata_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Load the data + + Note: this function may print a `FutureWarning` + """ + ) + return + + +@app.cell +def _(adata_filepath, read_h5ad): + adata = read_h5ad(adata_filepath) + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3.1. Preprocess the Data For Visualization + + This dataset contains 25,587 genes. We prepare to visualize the top 50 highly variable genes for the heatmap as ranked by dispersion norm, although one may use any boolean array filter for the heatmap. + """ + ) + return + + +@app.cell +def _(adata): + top_dispersion = adata.var["dispersions_norm"][ + sorted( + range(len(adata.var["dispersions_norm"])), + key=lambda k: adata.var["dispersions_norm"][k], + )[-51:][0] + ] + adata.var["top_highly_variable"] = ( + adata.var["dispersions_norm"] > top_dispersion + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3.2 Save the Data to Zarr store + + We want to convert the original `h5ad` file to a [Zarr](https://zarr.readthedocs.io/en/stable/) store, which Vitessce is able to load. We can use the `optimize_adata` function to ensure that all arrays and dataframe columns that we intend to use in our visualization are in the optimal format to be loaded by Vitessce. This function will cast arrays to numerical data types that take up less space (as long as the values allow). Note: unused arrays and columns (i.e., not specified in any of the parameters to `optimize_adata`) will not be copied into the new AnnData object. + """ + ) + return + + +@app.cell +def _(VAR_CHUNK_SIZE, adata, isdir, join): + zarr_filepath = join("data", "habib17.h5ad.zarr") + if not isdir(zarr_filepath): + adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE]) + return (zarr_filepath,) + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, cm, zarr_filepath): + vc = VitessceConfig( + schema_version="1.0.17", + name='Habib et al', + description='COVID-19 Healthy Donor Brain' + ) + + # Add data. + dataset = vc.add_dataset(name='Brain').add_object(AnnDataWrapper( + adata_path=zarr_filepath, + obs_embedding_paths=["obsm/X_umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/CellType"], + obs_set_names=["Cell Type"], + obs_feature_matrix_path="X", + initial_feature_filter_path="var/top_highly_variable", + coordination_values={ + "obsType": 'cell', + "featureType": 'gene', + "featureValueType": 'expression', + }, + )).add_object(AnnDataWrapper( + adata_path=zarr_filepath, + obs_feature_column_paths=["obs/percent_mito"], + coordination_values={ + "obsType": 'cell', + "featureType": 'qualityMetric', + "featureValueType": 'value', + } + )) + + # Add views. + scatterplot = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + scatterplot_2 = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + histogram = vc.add_view(cm.FEATURE_VALUE_HISTOGRAM, dataset=dataset) + + # Link views. + + # Color one of the two scatterplots by the percent_mito quality metric. + # Also use this quality metric for the histogram values. + vc.link_views_by_dict([histogram, scatterplot_2], { + "obsType": 'cell', + "featureType": 'qualityMetric', + "featureValueType": 'value', + "featureSelection": ["percent_mito"], + "obsColorEncoding": "geneSelection", + }, meta=False) + + # Synchronize the zooming and panning of the two scatterplots + vc.link_views_by_dict([scatterplot, scatterplot_2], { + "embeddingZoom": None, + "embeddingTargetX": None, + "embeddingTargetY": None, + }, meta=False) + + # Define the layout. + vc.layout((scatterplot | (cell_sets / genes)) / (scatterplot_2 | histogram)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Create the widget + + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_from_dict.ipynb b/docs/notebooks/widget_from_dict.ipynb deleted file mode 100644 index 868c8497..00000000 --- a/docs/notebooks/widget_from_dict.ipynb +++ /dev/null @@ -1,108 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Using an existing view config dict" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the `VitessceConfig` class." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import VitessceConfig" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Import config of interest as a dict\n", - "\n", - "The Vitessce config at its lowest level is a JSON object or a Python `dict` that specifies a layout for the Vitessce components will be rendered in the widget. These components may be scatterplots, spatial plots, heatmaps, or control components. The config also describes the datasets and files that will be visualized.\n", - "\n", - "The `vitessce` package provides helper functions and classes to simplify the process of defining Vitessce configs. Those functions are demonstrated in the other notebooks. The helper functions are intended to make visualization of local datasets easy. However, in this case, we are importing a config that has been pre-defined in the file `example_configs.py`, in which the dataset being visualized is stored remotely in AWS S3 (rather than locally)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from example_configs import dries as dries_config" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Create the Vitessce widget\n", - "\n", - "Create the widget by creating a new config instance using the `VitessceConfig.from_dict` static method, then calling the config's `.widget()` method.\n", - "\n", - "Render the widget by placing the widget variable on its own line at the end of the notebook cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig.from_dict(dries_config)\n", - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_from_dict.mo.py b/docs/notebooks/widget_from_dict.mo.py new file mode 100644 index 00000000..a9dd44ec --- /dev/null +++ b/docs/notebooks/widget_from_dict.mo.py @@ -0,0 +1,84 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Using an existing view config dict + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the `VitessceConfig` class. + """ + ) + return + + +@app.cell +def _(): + from vitessce import VitessceConfig + return (VitessceConfig,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Import config of interest as a dict + + The Vitessce config at its lowest level is a JSON object or a Python `dict` that specifies a layout for the Vitessce components will be rendered in the widget. These components may be scatterplots, spatial plots, heatmaps, or control components. The config also describes the datasets and files that will be visualized. + + The `vitessce` package provides helper functions and classes to simplify the process of defining Vitessce configs. Those functions are demonstrated in the other notebooks. The helper functions are intended to make visualization of local datasets easy. However, in this case, we are importing a config that has been pre-defined in the file `example_configs.py`, in which the dataset being visualized is stored remotely in AWS S3 (rather than locally). + """ + ) + return + + +@app.cell +def _(): + from example_configs import dries as dries_config + return (dries_config,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Create the Vitessce widget + + Create the widget by creating a new config instance using the `VitessceConfig.from_dict` static method, then calling the config's `.widget()` method. + + Render the widget by placing the widget variable on its own line at the end of the notebook cell. + """ + ) + return + + +@app.cell +def _(VitessceConfig, dries_config): + vc = VitessceConfig.from_dict(dries_config) + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_genomic_profiles.ipynb b/docs/notebooks/widget_genomic_profiles.ipynb deleted file mode 100644 index 1794598c..00000000 --- a/docs/notebooks/widget_genomic_profiles.ipynb +++ /dev/null @@ -1,220 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of genomic profiles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - " MultivecZarrWrapper,\n", - ")\n", - "from vitessce.data_utils import (\n", - " adata_to_multivec_zarr,\n", - ")\n", - "from os.path import join\n", - "from scipy.io import mmread\n", - "import pandas as pd\n", - "import numpy as np\n", - "from anndata import AnnData" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Load the data\n", - "\n", - "In this step, we load the raw data that has been downloaded from the HuBMAP portal https://portal.hubmapconsortium.org/browse/dataset/210d118a14c8624b6bb9610a9062656e" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mtx = mmread(join('data', 'snapatac', 'filtered_cell_by_bin.mtx')).toarray()\n", - "barcodes_df = pd.read_csv(join('data', 'snapatac', 'barcodes.txt'), header=None)\n", - "bins_df = pd.read_csv(join('data', 'snapatac', 'bins.txt'), header=None, names=[\"interval\"])\n", - "clusters_df = pd.read_csv(join('data', 'snapatac', 'umap_coords_clusters.csv'), index_col=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Convert the data to Vitessce-compatible formats\n", - "\n", - "Vitessce can load AnnData objects saved to Zarr formats efficiently." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# The genome assembly is GRCh38 but the chromosome names in the bin names do not start with the \"chr\" prefix.\n", - "# This is incompatible with the chromosome names from `negspy`, so we need to append the prefix.\n", - "bins_df[\"interval\"] = bins_df[\"interval\"].apply(lambda x: \"chr\" + x)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "obs = clusters_df[[\"cluster\"]]\n", - "obs[\"cluster\"] = obs[\"cluster\"].astype(str)\n", - "obsm = { \"X_umap\": clusters_df[[\"umap.1\", \"umap.2\"]].values }\n", - "adata = AnnData(X=mtx, obs=obs, var=bins_df, obsm=obsm)\n", - "adata" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "multivec_zarr_path = join(\"data\", \"HBM485.TBWH.322.multivec.zarr\")\n", - "adata_zarr_path = join(\"data\", \"HBM485.TBWH.322.adata.zarr\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Sort cluster IDs\n", - "cluster_ids = obs[\"cluster\"].unique().tolist()\n", - "cluster_ids.sort(key=int)\n", - "# Save genomic profiles to multivec-zarr format.\n", - "adata_to_multivec_zarr(adata, multivec_zarr_path, obs_set_col=\"cluster\", obs_set_name=\"Cluster\", obs_set_vals=cluster_ids)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Save anndata object to AnnData-Zarr format.\n", - "adata.write_zarr(adata_zarr_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "## 4. Make a Vitessce configuration\n", - "\n", - "We need to tell Vitessce about the data that we want to load and the visualization components that we want to include in the widget.\n", - "For this dataset, we want to add the `GENOMIC_PROFILES` component, which renders genome browser tracks with [HiGlass](http://higlass.io)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='HuBMAP snATAC-seq')\n", - "dataset = vc.add_dataset(name='HBM485.TBWH.322').add_object(MultivecZarrWrapper(\n", - " zarr_path=multivec_zarr_path\n", - ")).add_object(AnnDataWrapper(\n", - " adata_path=adata_zarr_path,\n", - " obs_embedding_paths=[\"obsm/X_umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/cluster\"],\n", - " obs_set_names=[\"Cluster\"],\n", - "))\n", - "\n", - "genomic_profiles = vc.add_view(vt.GENOMIC_PROFILES, dataset=dataset)\n", - "scatter = vc.add_view(vt.SCATTERPLOT, dataset=dataset, mapping = \"UMAP\")\n", - "cell_sets = vc.add_view(vt.OBS_SETS, dataset=dataset)\n", - "\n", - "vc.layout(genomic_profiles / (scatter | cell_sets));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Create the widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget(height=800)\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_genomic_profiles.mo.py b/docs/notebooks/widget_genomic_profiles.mo.py new file mode 100644 index 00000000..2c5bffce --- /dev/null +++ b/docs/notebooks/widget_genomic_profiles.mo.py @@ -0,0 +1,199 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of genomic profiles + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + ViewType as vt, + CoordinationType as ct, + AnnDataWrapper, + MultivecZarrWrapper, + ) + from vitessce.data_utils import ( + adata_to_multivec_zarr, + ) + from os.path import join + from scipy.io import mmread + import pandas as pd + import numpy as np + from anndata import AnnData + return ( + AnnData, + AnnDataWrapper, + MultivecZarrWrapper, + VitessceConfig, + adata_to_multivec_zarr, + join, + mmread, + pd, + vt, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Load the data + + In this step, we load the raw data that has been downloaded from the HuBMAP portal https://portal.hubmapconsortium.org/browse/dataset/210d118a14c8624b6bb9610a9062656e + """ + ) + return + + +@app.cell +def _(join, mmread, pd): + mtx = mmread(join('data', 'snapatac', 'filtered_cell_by_bin.mtx')).toarray() + barcodes_df = pd.read_csv(join('data', 'snapatac', 'barcodes.txt'), header=None) + bins_df = pd.read_csv(join('data', 'snapatac', 'bins.txt'), header=None, names=["interval"]) + clusters_df = pd.read_csv(join('data', 'snapatac', 'umap_coords_clusters.csv'), index_col=0) + return bins_df, clusters_df, mtx + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Convert the data to Vitessce-compatible formats + + Vitessce can load AnnData objects saved to Zarr formats efficiently. + """ + ) + return + + +@app.cell +def _(bins_df): + # The genome assembly is GRCh38 but the chromosome names in the bin names do not start with the "chr" prefix. + # This is incompatible with the chromosome names from `negspy`, so we need to append the prefix. + bins_df["interval"] = bins_df["interval"].apply(lambda x: "chr" + x) + return + + +@app.cell +def _(AnnData, bins_df, clusters_df, mtx): + obs = clusters_df[["cluster"]] + obs["cluster"] = obs["cluster"].astype(str) + obsm = { "X_umap": clusters_df[["umap.1", "umap.2"]].values } + adata = AnnData(X=mtx, obs=obs, var=bins_df, obsm=obsm) + adata + return adata, obs + + +@app.cell +def _(join): + multivec_zarr_path = join("data", "HBM485.TBWH.322.multivec.zarr") + adata_zarr_path = join("data", "HBM485.TBWH.322.adata.zarr") + return adata_zarr_path, multivec_zarr_path + + +@app.cell +def _(adata, adata_to_multivec_zarr, multivec_zarr_path, obs): + # Sort cluster IDs + cluster_ids = obs["cluster"].unique().tolist() + cluster_ids.sort(key=int) + # Save genomic profiles to multivec-zarr format. + adata_to_multivec_zarr(adata, multivec_zarr_path, obs_set_col="cluster", obs_set_name="Cluster", obs_set_vals=cluster_ids) + return + + +@app.cell +def _(adata, adata_zarr_path): + # Save anndata object to AnnData-Zarr format. + adata.write_zarr(adata_zarr_path) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Make a Vitessce configuration + + We need to tell Vitessce about the data that we want to load and the visualization components that we want to include in the widget. + For this dataset, we want to add the `GENOMIC_PROFILES` component, which renders genome browser tracks with [HiGlass](http://higlass.io). + """ + ) + return + + +@app.cell +def _( + AnnDataWrapper, + MultivecZarrWrapper, + VitessceConfig, + adata_zarr_path, + multivec_zarr_path, + vt, +): + vc = VitessceConfig(schema_version="1.0.15", name='HuBMAP snATAC-seq') + dataset = vc.add_dataset(name='HBM485.TBWH.322').add_object(MultivecZarrWrapper( + zarr_path=multivec_zarr_path + )).add_object(AnnDataWrapper( + adata_path=adata_zarr_path, + obs_embedding_paths=["obsm/X_umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/cluster"], + obs_set_names=["Cluster"], + )) + + genomic_profiles = vc.add_view(vt.GENOMIC_PROFILES, dataset=dataset) + scatter = vc.add_view(vt.SCATTERPLOT, dataset=dataset, mapping = "UMAP") + cell_sets = vc.add_view(vt.OBS_SETS, dataset=dataset) + + vc.layout(genomic_profiles / (scatter | cell_sets)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Create the widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget(height=800) + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_imaging.ipynb b/docs/notebooks/widget_imaging.ipynb deleted file mode 100644 index 9eed449f..00000000 --- a/docs/notebooks/widget_imaging.ipynb +++ /dev/null @@ -1,114 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of Multi-Modal Imaging Data\n", - "We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " OmeTiffWrapper,\n", - " MultiImageWrapper,\n", - ")\n", - "from os.path import join" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Configure Vitessce\n", - "Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", - "dataset = vc.add_dataset(name='Spraggins').add_object(\n", - " MultiImageWrapper(\n", - " image_wrappers=[\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode')\n", - " ],\n", - " use_physical_size_scaling=True,\n", - " )\n", - ")\n", - "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", - "status = vc.add_view(cm.STATUS, dataset=dataset)\n", - "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True)\n", - "vc.layout(spatial | (lc / status));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Create the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_imaging.mo.py b/docs/notebooks/widget_imaging.mo.py new file mode 100644 index 00000000..744a5e48 --- /dev/null +++ b/docs/notebooks/widget_imaging.mo.py @@ -0,0 +1,87 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of Multi-Modal Imaging Data + We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal. + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + OmeTiffWrapper, + MultiImageWrapper, + ) + from os.path import join + return MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Configure Vitessce + Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes. + """ + ) + return + + +@app.cell +def _(MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm): + vc = VitessceConfig(schema_version="1.0.15", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca') + dataset = vc.add_dataset(name='Spraggins').add_object( + MultiImageWrapper( + image_wrappers=[ + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode') + ], + use_physical_size_scaling=True, + ) + ) + spatial = vc.add_view(cm.SPATIAL, dataset=dataset) + status = vc.add_view(cm.STATUS, dataset=dataset) + lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True) + vc.layout(spatial | (lc / status)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Create the Vitessce widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_imaging_beta.ipynb b/docs/notebooks/widget_imaging_beta.ipynb deleted file mode 100644 index e0f78c5b..00000000 --- a/docs/notebooks/widget_imaging_beta.ipynb +++ /dev/null @@ -1,168 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of Multi-Modal Imaging Data\n", - "We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " OmeTiffWrapper,\n", - " MultiImageWrapper,\n", - " CoordinationLevel as CL,\n", - ")\n", - "from os.path import join" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Configure Vitessce\n", - "Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.16\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", - "dataset = vc.add_dataset(name='Spraggins').add_file(\n", - " url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=',\n", - " file_type=\"image.ome-tiff\",\n", - " coordination_values={\n", - " \"fileUid\": \"PAS\",\n", - " },\n", - ")\n", - "\n", - "imageScopes = vc.add_coordination_by_dict({\n", - " \"imageLayer\": CL([\n", - " {\n", - " \"fileUid\": 'PAS',\n", - " \"spatialLayerOpacity\": 1,\n", - " \"spatialLayerVisible\": True,\n", - " \"photometricInterpretation\": 'RGB',\n", - " \"imageChannel\": CL([\n", - " {\n", - " \"spatialTargetC\": 0,\n", - " \"spatialChannelColor\": [255, 0, 0],\n", - " \"spatialChannelVisible\": True,\n", - " \"spatialChannelOpacity\": 1.0,\n", - " \"spatialChannelWindow\": [0, 255],\n", - " },\n", - " {\n", - " \"spatialTargetC\": 1,\n", - " \"spatialChannelColor\": [0, 255, 0],\n", - " \"spatialChannelVisible\": True,\n", - " \"spatialChannelOpacity\": 1.0,\n", - " \"spatialChannelWindow\": [0, 255],\n", - " },\n", - " {\n", - " \"spatialTargetC\": 2,\n", - " \"spatialChannelColor\": [0, 0, 255],\n", - " \"spatialChannelVisible\": True,\n", - " \"spatialChannelOpacity\": 1.0,\n", - " \"spatialChannelWindow\": [0, 255],\n", - " },\n", - " ]),\n", - " }\n", - " ])\n", - "})\n", - "\n", - "metaCoordinationScope = vc.add_meta_coordination()\n", - "metaCoordinationScope.use_coordination_by_dict(imageScopes)\n", - "\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "lc = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "spatial.use_meta_coordination(metaCoordinationScope)\n", - "lc.use_meta_coordination(metaCoordinationScope)\n", - "\n", - "vc.layout(spatial | lc);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Create the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a5efa92286df4ad58cf94cfb3d12b703", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VitessceWidget(config={'version': '1.0.16', 'name': 'Spraggins Multi-Modal', 'description': 'PAS + IMS + AF Fr…" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "vw = vc.widget(custom_js_url='http://localhost:8000/packages/main/prod/dist/index.min.js')\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.9.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_imaging_beta.mo.py b/docs/notebooks/widget_imaging_beta.mo.py new file mode 100644 index 00000000..547bb0f8 --- /dev/null +++ b/docs/notebooks/widget_imaging_beta.mo.py @@ -0,0 +1,125 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of Multi-Modal Imaging Data + We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal. + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + OmeTiffWrapper, + MultiImageWrapper, + CoordinationLevel as CL, + ) + from os.path import join + return CL, VitessceConfig + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Configure Vitessce + Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes. + """ + ) + return + + +@app.cell +def _(CL, VitessceConfig): + vc = VitessceConfig(schema_version="1.0.16", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca') + dataset = vc.add_dataset(name='Spraggins').add_file( + url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', + file_type="image.ome-tiff", + coordination_values={ + "fileUid": "PAS", + }, + ) + + imageScopes = vc.add_coordination_by_dict({ + "imageLayer": CL([ + { + "fileUid": 'PAS', + "spatialLayerOpacity": 1, + "spatialLayerVisible": True, + "photometricInterpretation": 'RGB', + "imageChannel": CL([ + { + "spatialTargetC": 0, + "spatialChannelColor": [255, 0, 0], + "spatialChannelVisible": True, + "spatialChannelOpacity": 1.0, + "spatialChannelWindow": [0, 255], + }, + { + "spatialTargetC": 1, + "spatialChannelColor": [0, 255, 0], + "spatialChannelVisible": True, + "spatialChannelOpacity": 1.0, + "spatialChannelWindow": [0, 255], + }, + { + "spatialTargetC": 2, + "spatialChannelColor": [0, 0, 255], + "spatialChannelVisible": True, + "spatialChannelOpacity": 1.0, + "spatialChannelWindow": [0, 255], + }, + ]), + } + ]) + }) + + metaCoordinationScope = vc.add_meta_coordination() + metaCoordinationScope.use_coordination_by_dict(imageScopes) + + spatial = vc.add_view("spatialBeta", dataset=dataset) + lc = vc.add_view("layerControllerBeta", dataset=dataset) + + spatial.use_meta_coordination(metaCoordinationScope) + lc.use_meta_coordination(metaCoordinationScope) + + vc.layout(spatial | lc); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Create the Vitessce widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget(custom_js_url='http://localhost:8000/packages/main/prod/dist/index.min.js') + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_imaging_segmentation.ipynb b/docs/notebooks/widget_imaging_segmentation.ipynb deleted file mode 100644 index 7177e4d8..00000000 --- a/docs/notebooks/widget_imaging_segmentation.ipynb +++ /dev/null @@ -1,118 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of Segmentation Bitmask\n", - "We visualize raw imaging data + a segmentation bitmask the [MCMicro piplene](https://mcmicro.org/) - see https://www.biorxiv.org/content/10.1101/2021.03.15.435473v1.full and specifically [Figure S1](https://www.google.com/url?q=https://www.biorxiv.org/content/10.1101/2021.03.15.435473v1.full%23F3&sa=D&source=editors&ust=1623173627976000&usg=AOvVaw3JkzCxYyE86q8jxfNCgShh)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " OmeTiffWrapper,\n", - " MultiImageWrapper,\n", - ")\n", - "from os.path import join" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Configure Vitessce\n", - "Set up the two images, already pyramidal from the [bioformats2raw + raw2ometiff pipeline](https://github.com/hms-dbmi/viv/tree/master/tutorial), labeling the segmentation \"on top\" as the bitmask and the other as simply the image data. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='MCMicro Bitmask Visualization', description='Segmentation + Data of Exemplar 001')\n", - "dataset = vc.add_dataset(name='MCMicro').add_object(\n", - " MultiImageWrapper(\n", - " image_wrappers=[\n", - " OmeTiffWrapper(img_url='https://vitessce-demo-data.storage.googleapis.com/exemplar-001/exemplar-001.pyramid.ome.tif', name='Image'),\n", - " OmeTiffWrapper(img_url='https://vitessce-demo-data.storage.googleapis.com/exemplar-001/cellMask.pyramid.ome.tif', name='Mask', is_bitmask=True),\n", - " ]\n", - " )\n", - ")\n", - "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", - "status = vc.add_view(cm.STATUS, dataset=dataset)\n", - "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset)\n", - "vc.layout(spatial | (lc / status));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Create the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_imaging_segmentation.mo.py b/docs/notebooks/widget_imaging_segmentation.mo.py new file mode 100644 index 00000000..4d4b2177 --- /dev/null +++ b/docs/notebooks/widget_imaging_segmentation.mo.py @@ -0,0 +1,84 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of Segmentation Bitmask + We visualize raw imaging data + a segmentation bitmask the [MCMicro piplene](https://mcmicro.org/) - see https://www.biorxiv.org/content/10.1101/2021.03.15.435473v1.full and specifically [Figure S1](https://www.google.com/url?q=https://www.biorxiv.org/content/10.1101/2021.03.15.435473v1.full%23F3&sa=D&source=editors&ust=1623173627976000&usg=AOvVaw3JkzCxYyE86q8jxfNCgShh) + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + OmeTiffWrapper, + MultiImageWrapper, + ) + from os.path import join + return MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Configure Vitessce + Set up the two images, already pyramidal from the [bioformats2raw + raw2ometiff pipeline](https://github.com/hms-dbmi/viv/tree/master/tutorial), labeling the segmentation "on top" as the bitmask and the other as simply the image data. + """ + ) + return + + +@app.cell +def _(MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm): + vc = VitessceConfig(schema_version="1.0.15", name='MCMicro Bitmask Visualization', description='Segmentation + Data of Exemplar 001') + dataset = vc.add_dataset(name='MCMicro').add_object( + MultiImageWrapper( + image_wrappers=[ + OmeTiffWrapper(img_url='https://vitessce-demo-data.storage.googleapis.com/exemplar-001/exemplar-001.pyramid.ome.tif', name='Image'), + OmeTiffWrapper(img_url='https://vitessce-demo-data.storage.googleapis.com/exemplar-001/cellMask.pyramid.ome.tif', name='Mask', is_bitmask=True), + ] + ) + ) + spatial = vc.add_view(cm.SPATIAL, dataset=dataset) + status = vc.add_view(cm.STATUS, dataset=dataset) + lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset) + vc.layout(spatial | (lc / status)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Create the Vitessce widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_loom.ipynb b/docs/notebooks/widget_loom.ipynb deleted file mode 100644 index 0c4f958b..00000000 --- a/docs/notebooks/widget_loom.ipynb +++ /dev/null @@ -1,219 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of a Loom file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_loom\n", - "import numpy as np\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")\n", - "from vitessce.data_utils import (\n", - " optimize_adata,\n", - " to_diamond,\n", - " VAR_CHUNK_SIZE,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Download data\n", - "\n", - "Download `osmFISH_SScortex_mouse_all_cells.loom` from http://loom.linnarssonlab.org/." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "loom_filepath = join(\"data\", \"osmFISH_SScortex_mouse_all_cells.loom\")\n", - "if not isfile(loom_filepath):\n", - " os.makedirs(\"data\", exist_ok=True)\n", - " urlretrieve('http://loom.linnarssonlab.org/clone/osmFISH/osmFISH_SScortex_mouse_all_cells.loom', loom_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Open Loom file with AnnData's read_loom" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = read_loom(loom_filepath, obsm_names={\"tSNE\": [\"_tSNE_1\", \"_tSNE_2\"], \"spatial\": [\"X\", \"Y\"]})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Generate pseudo-segmentations as diamond-shaped polygons centered on the spatial coordinate of each cell, and store in `adata.obsm[\"segmentations\"]`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "num_cells = adata.obs.shape[0]\n", - "adata.obsm[\"segmentations\"] = np.zeros((num_cells, 4, 2))\n", - "radius = 100\n", - "for i in range(num_cells):\n", - " adata.obsm[\"segmentations\"][i, :, :] = to_diamond(adata.obsm['spatial'][i, 0], adata.obsm['spatial'][i, 1], radius)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Save the AnnData object to a Zarr store:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zarr_filepath = join(\"data\", \"osmFISH_SScortex_mouse_all_cells.zarr\")\n", - "if not isdir(zarr_filepath) or True:\n", - " adata = optimize_adata(\n", - " adata,\n", - " obs_cols=[\"ClusterName\"],\n", - " obsm_keys=[\"tSNE\", \"spatial\", \"segmentations\"],\n", - " optimize_X=True,\n", - " )\n", - " adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Configure Vitessce\n", - "\n", - "Create a Vitessce view config." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Loom Example', description='osmFISH dataset of the mouse cortex including all cells')\n", - "w = AnnDataWrapper(adata_path=zarr_filepath, obs_set_paths=[\"obs/ClusterName\"], obs_set_names=[\"Clusters\"], obs_locations_path=\"obsm/spatial\", obs_segmentations_path=\"obsm/segmentations\", obs_embedding_paths=[\"obsm/tSNE\"])\n", - "dataset = vc.add_dataset(name='SScortex').add_object(w)\n", - "\n", - "tsne = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"tSNE\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", - "\n", - "spatial_segmentation_layer_value = {\n", - " \"opacity\": 1,\n", - " \"radius\": 0,\n", - " \"visible\": True,\n", - " \"stroked\": False\n", - "}\n", - "\n", - "vc.link_views([spatial], [ct.SPATIAL_ZOOM, ct.SPATIAL_TARGET_X, ct.SPATIAL_TARGET_Y, ct.SPATIAL_SEGMENTATION_LAYER], [-6.43, 10417.69, 24885.55, spatial_segmentation_layer_value])\n", - "vc.layout(spatial | (tsne / cell_sets));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Render the widget" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A widget can be created with the `.widget()` method on the config instance." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_loom.mo.py b/docs/notebooks/widget_loom.mo.py new file mode 100644 index 00000000..a9ff13f7 --- /dev/null +++ b/docs/notebooks/widget_loom.mo.py @@ -0,0 +1,208 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of a Loom file + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + from anndata import read_loom + import numpy as np + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + from vitessce.data_utils import ( + optimize_adata, + to_diamond, + VAR_CHUNK_SIZE, + ) + return ( + AnnDataWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + cm, + ct, + isdir, + isfile, + join, + np, + optimize_adata, + os, + read_loom, + to_diamond, + urlretrieve, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Download data + + Download `osmFISH_SScortex_mouse_all_cells.loom` from http://loom.linnarssonlab.org/. + """ + ) + return + + +@app.cell +def _(isfile, join, os, urlretrieve): + loom_filepath = join("data", "osmFISH_SScortex_mouse_all_cells.loom") + if not isfile(loom_filepath): + os.makedirs("data", exist_ok=True) + urlretrieve('http://loom.linnarssonlab.org/clone/osmFISH/osmFISH_SScortex_mouse_all_cells.loom', loom_filepath) + return (loom_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Open Loom file with AnnData's read_loom + """ + ) + return + + +@app.cell +def _(loom_filepath, read_loom): + adata = read_loom(loom_filepath, obsm_names={"tSNE": ["_tSNE_1", "_tSNE_2"], "spatial": ["X", "Y"]}) + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Generate pseudo-segmentations as diamond-shaped polygons centered on the spatial coordinate of each cell, and store in `adata.obsm["segmentations"]` + """ + ) + return + + +@app.cell +def _(adata, np, to_diamond): + num_cells = adata.obs.shape[0] + adata.obsm["segmentations"] = np.zeros((num_cells, 4, 2)) + radius = 100 + for i in range(num_cells): + adata.obsm["segmentations"][i, :, :] = to_diamond(adata.obsm['spatial'][i, 0], adata.obsm['spatial'][i, 1], radius) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + Save the AnnData object to a Zarr store: + """ + ) + return + + +@app.cell +def _(VAR_CHUNK_SIZE, adata, isdir, join, optimize_adata): + zarr_filepath = join('data', 'osmFISH_SScortex_mouse_all_cells.zarr') + if not isdir(zarr_filepath) or True: + adata_1 = optimize_adata(adata, obs_cols=['ClusterName'], obsm_keys=['tSNE', 'spatial', 'segmentations'], optimize_X=True) + adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE]) + return (zarr_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Configure Vitessce + + Create a Vitessce view config. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, cm, ct, zarr_filepath): + vc = VitessceConfig(schema_version="1.0.15", name='Loom Example', description='osmFISH dataset of the mouse cortex including all cells') + w = AnnDataWrapper(adata_path=zarr_filepath, obs_set_paths=["obs/ClusterName"], obs_set_names=["Clusters"], obs_locations_path="obsm/spatial", obs_segmentations_path="obsm/segmentations", obs_embedding_paths=["obsm/tSNE"]) + dataset = vc.add_dataset(name='SScortex').add_object(w) + + tsne = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="tSNE") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + spatial = vc.add_view(cm.SPATIAL, dataset=dataset) + + spatial_segmentation_layer_value = { + "opacity": 1, + "radius": 0, + "visible": True, + "stroked": False + } + + vc.link_views([spatial], [ct.SPATIAL_ZOOM, ct.SPATIAL_TARGET_X, ct.SPATIAL_TARGET_Y, ct.SPATIAL_SEGMENTATION_LAYER], [-6.43, 10417.69, 24885.55, spatial_segmentation_layer_value]) + vc.layout(spatial | (tsne / cell_sets)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Render the widget + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + A widget can be created with the `.widget()` method on the config instance. + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_modify_config.ipynb b/docs/notebooks/widget_modify_config.ipynb deleted file mode 100644 index 97355f6d..00000000 --- a/docs/notebooks/widget_modify_config.ipynb +++ /dev/null @@ -1,1153 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "dI4N5oh0cYc1", - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Programmatic modification of widget configuration" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "KnWYWhnMcYc2" - }, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " OmeTiffWrapper,\n", - " MultiImageWrapper,\n", - " CoordinationLevel as CL,\n", - " ObsSegmentationsOmeTiffWrapper,\n", - " ImageOmeTiffWrapper,\n", - " get_initial_coordination_scope_prefix,\n", - ")\n", - "import random" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "qqiLSqvqcYc3" - }, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.16\")\n", - "dataset = vc.add_dataset(name='Spraggins').add_object(\n", - " ImageOmeTiffWrapper(\n", - " img_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.ome.tif\",\n", - " offsets_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.offsets.json\"\n", - " )\n", - ").add_object(\n", - " ObsSegmentationsOmeTiffWrapper(\n", - " img_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.ome.tif\",\n", - " offsets_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.offsets.json\",\n", - " obs_types_from_channel_names=True\n", - " )\n", - ")\n", - "\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "lc = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, lc], {\n", - " \"imageLayer\": CL([\n", - " {\n", - " \"photometricInterpretation\": \"RGB\"\n", - " }\n", - " ]),\n", - "}, meta=True, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.layout(spatial | lc);" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 617, - "referenced_widgets": [ - "a6e72904c7cd4521bb486f69ac9cb6c7", - "800201ad9e7942fbbb6b0a195ef6ec6a" - ] - }, - "id": "wRXsCajecYc3", - "outputId": "726b6ad0-5f3f-4b31-922f-a96336e688f2" - }, - "outputs": [], - "source": [ - "vw = vc.widget(remount_on_uid_change=False)\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "H43CtKancu6-", - "outputId": "00d1a53e-75fe-4fe8-c5a7-33c175fddbc4" - }, - "outputs": [], - "source": [ - "# Inspect the current configuration value.\n", - "# This is a dict in the JSON-based format https://vitessce.io/docs/view-config-json/\n", - "vw.config" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "kU1rj0EPcYc3" - }, - "outputs": [], - "source": [ - "# Programatically set a different zoom level and toggle the visibility/color of different segmentation layers:\n", - "vw.config = {\n", - " **vw.config,\n", - " # Need to provide a fresh \"uid\" value.\n", - " # This will tell Vitessce that the contents should be diff-ed against the previous config.\n", - " \"uid\": f\"new_config_{random.random()}\",\n", - " \"coordinationSpace\": {\n", - " # Information about the coordination space can be found at https://vitessce.io/docs/coordination-types/\n", - " **vw.config[\"coordinationSpace\"],\n", - " \"spatialZoom\": {\n", - " **vw.config[\"coordinationSpace\"][\"spatialZoom\"],\n", - " \"A\": -8\n", - " },\n", - " \"spatialChannelVisible\": {\n", - " **vw.config[\"coordinationSpace\"][\"spatialChannelVisible\"],\n", - " \"init_A_obsSegmentations_0\": True,\n", - " \"init_A_obsSegmentations_1\": False,\n", - " \"init_A_obsSegmentations_2\": False,\n", - " \"init_A_obsSegmentations_3\": False\n", - " },\n", - " \"spatialChannelColor\": {\n", - " **vw.config[\"coordinationSpace\"][\"spatialChannelColor\"],\n", - " \"init_A_obsSegmentations_0\": [255, 0, 0],\n", - " }\n", - " }\n", - "}" - ] - } - ], - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "800201ad9e7942fbbb6b0a195ef6ec6a": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a6e72904c7cd4521bb486f69ac9cb6c7": { - "model_module": "anywidget", - "model_module_version": "~0.9.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "vitessce.widget.VitessceWidget", - "_dom_classes": [], - "_esm": "\nimport { importWithMap } from 'https://unpkg.com/dynamic-importmap@0.1.0';\nconst importMap = {\n imports: {\n \"react\": \"https://esm.sh/react@18.2.0?dev\",\n \"react-dom\": \"https://esm.sh/react-dom@18.2.0?dev\",\n \"react-dom/client\": \"https://esm.sh/react-dom@18.2.0/client?dev\",\n },\n};\n\nconst React = await importWithMap(\"react\", importMap);\nconst { createRoot } = await importWithMap(\"react-dom/client\", importMap);\n\nconst e = React.createElement;\n\nconst prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;\n\n// The jupyter server may be running through a proxy,\n// which means that the client needs to prepend the part of the URL before /proxy/8000 such as\n// https://hub.gke2.mybinder.org/user/vitessce-vitessce-python-swi31vcv/proxy/8000/A/0/cells\nfunction prependBaseUrl(config, proxy, hasHostName) {\n if(!proxy || hasHostName) {\n return config;\n }\n const { origin } = new URL(window.location.href);\n let baseUrl;\n const jupyterLabConfigEl = document.getElementById('jupyter-config-data');\n\n if (jupyterLabConfigEl) {\n // This is jupyter lab\n baseUrl = JSON.parse(jupyterLabConfigEl.textContent || '').baseUrl;\n } else {\n // This is jupyter notebook\n baseUrl = document.getElementsByTagName('body')[0].getAttribute('data-base-url');\n }\n return {\n ...config,\n datasets: config.datasets.map(d => ({\n ...d,\n files: d.files.map(f => ({\n ...f,\n url: `${origin}${baseUrl}${f.url}`,\n })),\n })),\n };\n}\n\nasync function render(view) {\n const cssUid = view.model.get('uid');\n const jsDevMode = view.model.get('js_dev_mode');\n const jsPackageVersion = view.model.get('js_package_version');\n const customJsUrl = view.model.get('custom_js_url');\n const pluginEsmArr = view.model.get('plugin_esm');\n const remountOnUidChange = view.model.get('remount_on_uid_change');\n const storeUrls = view.model.get('store_urls');\n const invokeTimeout = view.model.get('invoke_timeout');\n\n const pkgName = (jsDevMode ? \"@vitessce/dev\" : \"vitessce\");\n\n importMap.imports[\"vitessce\"] = (customJsUrl.length > 0\n ? customJsUrl\n : `https://unpkg.com/${pkgName}@${jsPackageVersion}`\n );\n\n const {\n Vitessce,\n PluginFileType,\n PluginViewType,\n PluginCoordinationType,\n PluginJointFileType,\n z,\n useCoordination,\n useGridItemSize,\n // TODO: names and function signatures are subject to change for the following functions\n // Reference: https://github.com/keller-mark/use-coordination/issues/37#issuecomment-1946226827\n useComplexCoordination,\n useMultiCoordinationScopesNonNull,\n useMultiCoordinationScopesSecondaryNonNull,\n useComplexCoordinationSecondary,\n useCoordinationScopes,\n useCoordinationScopesBy,\n } = await importWithMap(\"vitessce\", importMap);\n\n let pluginViewTypes = [];\n let pluginCoordinationTypes = [];\n let pluginFileTypes = [];\n let pluginJointFileTypes = [];\n\n const stores = Object.fromEntries(\n storeUrls.map(storeUrl => ([\n storeUrl,\n {\n async get(key) {\n const [data, buffers] = await view.experimental.invoke(\"_zarr_get\", [storeUrl, key], {\n signal: AbortSignal.timeout(invokeTimeout),\n });\n if (!data.success) return undefined;\n\n if (key.includes(\"spatialdata_attrs\") && key.endsWith(\"0\") && !ArrayBuffer.isView(buffers[0].buffer)) {\n // For some reason, the Zarrita.js UnicodeStringArray does not seem to work with\n // ArrayBuffers (throws a TypeError), so here we convert to Uint8Array if needed.\n // This error is occurring specifically for the arr.getChunk call within the AnnDataSource._loadString function.\n // TODO: figure out a more long-term solution.\n return new Uint8Array(buffers[0].buffer);\n }\n\n return buffers[0].buffer;\n },\n }\n ])),\n );\n\n function invokePluginCommand(commandName, commandParams, commandBuffers) {\n return view.experimental.invoke(\"_plugin_command\", [commandName, commandParams], {\n signal: AbortSignal.timeout(invokeTimeout),\n ...(commandBuffers ? { buffers: commandBuffers } : {}),\n });\n }\n\n for (const pluginEsm of pluginEsmArr) {\n try {\n const pluginEsmUrl = URL.createObjectURL(new Blob([pluginEsm], { type: \"text/javascript\" }));\n const pluginModule = (await import(pluginEsmUrl)).default;\n URL.revokeObjectURL(pluginEsmUrl);\n\n const pluginsObj = await pluginModule.createPlugins({\n React,\n PluginFileType,\n PluginViewType,\n PluginCoordinationType,\n PluginJointFileType,\n z,\n invokeCommand: invokePluginCommand,\n useCoordination,\n useGridItemSize,\n useComplexCoordination,\n useMultiCoordinationScopesNonNull,\n useMultiCoordinationScopesSecondaryNonNull,\n useComplexCoordinationSecondary,\n useCoordinationScopes,\n useCoordinationScopesBy,\n });\n if(Array.isArray(pluginsObj.pluginViewTypes)) {\n pluginViewTypes = [...pluginViewTypes, ...pluginsObj.pluginViewTypes];\n }\n if(Array.isArray(pluginsObj.pluginCoordinationTypes)) {\n pluginCoordinationTypes = [...pluginCoordinationTypes, ...pluginsObj.pluginCoordinationTypes];\n }\n if(Array.isArray(pluginsObj.pluginFileTypes)) {\n pluginFileTypes = [...pluginFileTypes, ...pluginsObj.pluginFileTypes];\n }\n if(Array.isArray(pluginsObj.pluginJointFileTypes)) {\n pluginJointFileTypes = [...pluginJointFileTypes, ...pluginsObj.pluginJointFileTypes];\n }\n } catch(e) {\n console.error(e);\n }\n }\n\n function VitessceWidget(props) {\n const { model } = props;\n\n const [config, setConfig] = React.useState(prependBaseUrl(model.get('config'), model.get('proxy'), model.get('has_host_name')));\n const [validateConfig, setValidateConfig] = React.useState(true);\n const height = model.get('height');\n const theme = model.get('theme') === 'auto' ? (prefersDark ? 'dark' : 'light') : model.get('theme');\n\n const divRef = React.useRef();\n\n React.useEffect(() => {\n if(!divRef.current) {\n return () => {};\n }\n\n function handleMouseEnter() {\n const jpn = divRef.current.closest('.jp-Notebook');\n if(jpn) {\n jpn.style.overflow = \"hidden\";\n }\n }\n function handleMouseLeave(event) {\n if(event.relatedTarget === null || (event.relatedTarget && event.relatedTarget.closest('.jp-Notebook')?.length)) return;\n const jpn = divRef.current.closest('.jp-Notebook');\n if(jpn) {\n jpn.style.overflow = \"auto\";\n }\n }\n divRef.current.addEventListener(\"mouseenter\", handleMouseEnter);\n divRef.current.addEventListener(\"mouseleave\", handleMouseLeave);\n\n return () => {\n if(divRef.current) {\n divRef.current.removeEventListener(\"mouseenter\", handleMouseEnter);\n divRef.current.removeEventListener(\"mouseleave\", handleMouseLeave);\n }\n };\n }, [divRef]);\n\n // Config changed on JS side (from within ),\n // send updated config to Python side.\n const onConfigChange = React.useCallback((config) => {\n model.set('config', config);\n setValidateConfig(false);\n model.save_changes();\n }, [model]);\n\n // Config changed on Python side,\n // pass to component to it is updated on JS side.\n React.useEffect(() => {\n model.on('change:config', () => {\n const newConfig = prependBaseUrl(model.get('config'), model.get('proxy'), model.get('has_host_name'));\n\n // Force a re-render and re-validation by setting a new config.uid value.\n // TODO: make this conditional on a parameter from Python.\n //newConfig.uid = `random-${Math.random()}`;\n //console.log('newConfig', newConfig);\n setConfig(newConfig);\n });\n }, []);\n\n const vitessceProps = {\n height, theme, config, onConfigChange, validateConfig,\n pluginViewTypes, pluginCoordinationTypes, pluginFileTypes, pluginJointFileTypes,\n remountOnUidChange, stores,\n };\n\n return e('div', { ref: divRef, style: { height: height + 'px' } },\n e(React.Suspense, { fallback: e('div', {}, 'Loading...') },\n e(React.StrictMode, {},\n e(Vitessce, vitessceProps)\n ),\n ),\n );\n }\n\n const root = createRoot(view.el);\n root.render(e(VitessceWidget, { model: view.model }));\n\n return () => {\n // Re-enable scrolling.\n const jpn = view.el.closest('.jp-Notebook');\n if(jpn) {\n jpn.style.overflow = \"auto\";\n }\n\n // Clean up React and DOM state.\n root.unmount();\n if(view._isFromDisplay) {\n view.el.remove();\n }\n };\n}\nexport default { render };\n", - "_model_module": "anywidget", - "_model_module_version": "~0.9.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.9.*", - "_view_name": "AnyView", - "config": { - "coordinationSpace": { - "additionalObsSets": { - "A": null - }, - "dataset": { - "A": "A", - "init_A_image_0": "init_A_image_0", - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0" - }, - "featureHighlight": { - "A": null - }, - "featureSelection": { - "A": null - }, - "featureType": { - "A": "gene" - }, - "featureValueColormap": { - "A": "plasma" - }, - "featureValueColormapRange": { - "A": [ - 0, - 1 - ] - }, - "featureValueType": { - "A": "expression" - }, - "fileUid": { - "A": null, - "init_A_image_0": null, - "init_A_obsSegmentations_0": null - }, - "imageChannel": { - "A": null, - "init_A_image_0": "__dummy__", - "init_A_image_1": "__dummy__", - "init_A_image_2": "__dummy__" - }, - "imageLayer": { - "A": null, - "init_A_image_0": "__dummy__" - }, - "legendVisible": { - "A": true - }, - "metaCoordinationScopes": { - "init_A_image_0": { - "imageLayer": [ - "init_A_image_0" - ], - "spatialImageLayer": "init_A_image_0", - "spatialTargetT": "init_A_image_0", - "spatialTargetZ": "init_A_image_0" - }, - "init_A_obsSegmentations_0": { - "segmentationLayer": [ - "init_A_obsSegmentations_0" - ], - "spatialSegmentationLayer": "init_A_obsSegmentations_0", - "spatialTargetT": "init_A_obsSegmentations_0", - "spatialTargetZ": "init_A_obsSegmentations_0" - } - }, - "metaCoordinationScopesBy": { - "init_A_image_0": { - "imageChannel": { - "spatialChannelColor": { - "init_A_image_0": "init_A_image_0", - "init_A_image_1": "init_A_image_1", - "init_A_image_2": "init_A_image_2" - }, - "spatialChannelOpacity": { - "init_A_image_0": "init_A_image_0", - "init_A_image_1": "init_A_image_1", - "init_A_image_2": "init_A_image_2" - }, - "spatialChannelVisible": { - "init_A_image_0": "init_A_image_0", - "init_A_image_1": "init_A_image_1", - "init_A_image_2": "init_A_image_2" - }, - "spatialChannelWindow": { - "init_A_image_0": "init_A_image_0", - "init_A_image_1": "init_A_image_1", - "init_A_image_2": "init_A_image_2" - }, - "spatialTargetC": { - "init_A_image_0": "init_A_image_0", - "init_A_image_1": "init_A_image_1", - "init_A_image_2": "init_A_image_2" - } - }, - "imageLayer": { - "fileUid": { - "init_A_image_0": "init_A_image_0" - }, - "imageChannel": { - "init_A_image_0": [ - "init_A_image_0", - "init_A_image_1", - "init_A_image_2" - ] - }, - "photometricInterpretation": { - "init_A_image_0": "init_A_image_0" - }, - "spatialLayerOpacity": { - "init_A_image_0": "init_A_image_0" - }, - "spatialLayerVisible": { - "init_A_image_0": "init_A_image_0" - }, - "spatialTargetResolution": { - "init_A_image_0": "init_A_image_0" - }, - "volumetricRenderingAlgorithm": { - "init_A_image_0": "init_A_image_0" - } - } - }, - "init_A_obsSegmentations_0": { - "segmentationChannel": { - "obsColorEncoding": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "obsHighlight": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "obsType": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "spatialChannelColor": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "spatialChannelOpacity": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "spatialChannelVisible": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "spatialChannelWindow": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "spatialSegmentationFilled": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "spatialSegmentationStrokeWidth": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - }, - "spatialTargetC": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1": "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2": "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3": "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4": "init_A_obsSegmentations_4" - } - }, - "segmentationLayer": { - "fileUid": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0" - }, - "segmentationChannel": { - "init_A_obsSegmentations_0": [ - "init_A_obsSegmentations_0", - "init_A_obsSegmentations_1", - "init_A_obsSegmentations_2", - "init_A_obsSegmentations_3", - "init_A_obsSegmentations_4" - ] - }, - "spatialLayerOpacity": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0" - }, - "spatialLayerVisible": { - "init_A_obsSegmentations_0": "init_A_obsSegmentations_0" - } - } - } - }, - "moleculeHighlight": { - "A": null - }, - "obsColorEncoding": { - "A": "cellSetSelection", - "init_A_obsSegmentations_0": "spatialChannelColor", - "init_A_obsSegmentations_1": "spatialChannelColor", - "init_A_obsSegmentations_2": "spatialChannelColor", - "init_A_obsSegmentations_3": "spatialChannelColor", - "init_A_obsSegmentations_4": "spatialChannelColor" - }, - "obsFilter": { - "A": null - }, - "obsHighlight": { - "A": null, - "init_A_obsSegmentations_0": null, - "init_A_obsSegmentations_1": null, - "init_A_obsSegmentations_2": null, - "init_A_obsSegmentations_3": null, - "init_A_obsSegmentations_4": null - }, - "obsLabelsType": { - "A": null - }, - "obsSetColor": { - "A": null - }, - "obsSetHighlight": { - "A": null - }, - "obsSetSelection": { - "A": null - }, - "obsType": { - "A": "cell", - "init_A_obsSegmentations_0": "Channel 0", - "init_A_obsSegmentations_1": "Channel 1", - "init_A_obsSegmentations_2": "Channel 2", - "init_A_obsSegmentations_3": "Channel 3", - "init_A_obsSegmentations_4": "Channel 4" - }, - "photometricInterpretation": { - "A": null, - "init_A_image_0": "RGB" - }, - "pixelHighlight": { - "A": null - }, - "pointLayer": { - "A": null - }, - "segmentationChannel": { - "A": null, - "init_A_obsSegmentations_0": "__dummy__", - "init_A_obsSegmentations_1": "__dummy__", - "init_A_obsSegmentations_2": "__dummy__", - "init_A_obsSegmentations_3": "__dummy__", - "init_A_obsSegmentations_4": "__dummy__" - }, - "segmentationLayer": { - "A": null, - "init_A_obsSegmentations_0": "__dummy__" - }, - "spatialAxisFixed": { - "A": false - }, - "spatialChannelColor": { - "A": [ - 255, - 255, - 255 - ], - "init_A_image_0": [ - 255, - 255, - 255 - ], - "init_A_image_1": [ - 255, - 255, - 255 - ], - "init_A_image_2": [ - 255, - 255, - 255 - ], - "init_A_obsSegmentations_0": [ - 255, - 0, - 0 - ], - "init_A_obsSegmentations_1": [ - 0, - 255, - 0 - ], - "init_A_obsSegmentations_2": [ - 255, - 0, - 255 - ], - "init_A_obsSegmentations_3": [ - 255, - 255, - 0 - ], - "init_A_obsSegmentations_4": [ - 0, - 0, - 0 - ] - }, - "spatialChannelLabelSize": { - "A": 14 - }, - "spatialChannelLabelsOrientation": { - "A": "vertical" - }, - "spatialChannelLabelsVisible": { - "A": true - }, - "spatialChannelOpacity": { - "A": 1, - "init_A_image_0": 1, - "init_A_image_1": 1, - "init_A_image_2": 1, - "init_A_obsSegmentations_0": 1, - "init_A_obsSegmentations_1": 1, - "init_A_obsSegmentations_2": 1, - "init_A_obsSegmentations_3": 1, - "init_A_obsSegmentations_4": 1 - }, - "spatialChannelVisible": { - "A": true, - "init_A_image_0": true, - "init_A_image_1": true, - "init_A_image_2": true, - "init_A_obsSegmentations_0": true, - "init_A_obsSegmentations_1": false, - "init_A_obsSegmentations_2": false, - "init_A_obsSegmentations_3": false, - "init_A_obsSegmentations_4": true - }, - "spatialChannelWindow": { - "A": null, - "init_A_image_0": null, - "init_A_image_1": null, - "init_A_image_2": null, - "init_A_obsSegmentations_0": null, - "init_A_obsSegmentations_1": null, - "init_A_obsSegmentations_2": null, - "init_A_obsSegmentations_3": null, - "init_A_obsSegmentations_4": null - }, - "spatialImageLayer": { - "init_A_image_0": [ - { - "channels": [ - { - "color": [ - 255, - 0, - 0 - ], - "selection": { - "c": 0, - "t": 0, - "z": 0 - }, - "slider": [ - 0, - 255 - ], - "visible": true - }, - { - "color": [ - 0, - 255, - 0 - ], - "selection": { - "c": 1, - "t": 0, - "z": 0 - }, - "slider": [ - 0, - 255 - ], - "visible": true - }, - { - "color": [ - 0, - 0, - 255 - ], - "selection": { - "c": 2, - "t": 0, - "z": 0 - }, - "slider": [ - 0, - 255 - ], - "visible": true - } - ], - "colormap": null, - "domainType": "Min/Max", - "index": 0, - "modelMatrix": [ - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1 - ], - "opacity": 1, - "renderingMode": "Additive", - "transparentColor": null, - "type": "raster", - "use3d": false, - "visible": true - } - ] - }, - "spatialLayerColor": { - "A": null - }, - "spatialLayerColormap": { - "A": null - }, - "spatialLayerModelMatrix": { - "A": null - }, - "spatialLayerOpacity": { - "A": 1, - "init_A_image_0": 1, - "init_A_obsSegmentations_0": 1 - }, - "spatialLayerTransparentColor": { - "A": null - }, - "spatialLayerVisible": { - "A": true, - "init_A_image_0": true, - "init_A_obsSegmentations_0": true - }, - "spatialNeighborhoodLayer": { - "A": null - }, - "spatialOrbitAxis": { - "A": "Y" - }, - "spatialPointLayer": { - "A": null - }, - "spatialRenderingMode": { - "A": "2D" - }, - "spatialRotation": { - "A": 0 - }, - "spatialRotationOrbit": { - "A": 0 - }, - "spatialRotationX": { - "A": 0 - }, - "spatialRotationY": { - "A": 0 - }, - "spatialRotationZ": { - "A": 0 - }, - "spatialSegmentationFilled": { - "A": true, - "init_A_obsSegmentations_0": true, - "init_A_obsSegmentations_1": true, - "init_A_obsSegmentations_2": true, - "init_A_obsSegmentations_3": true, - "init_A_obsSegmentations_4": true - }, - "spatialSegmentationLayer": { - "init_A_obsSegmentations_0": [ - { - "channels": [ - { - "color": [ - 0, - 0, - 255 - ], - "selection": { - "c": 0, - "t": 0, - "z": 0 - }, - "slider": [ - 1, - 1 - ], - "visible": true - }, - { - "color": [ - 0, - 255, - 0 - ], - "selection": { - "c": 1, - "t": 0, - "z": 0 - }, - "slider": [ - 1, - 41 - ], - "visible": true - }, - { - "color": [ - 255, - 0, - 255 - ], - "selection": { - "c": 2, - "t": 0, - "z": 0 - }, - "slider": [ - 1, - 19 - ], - "visible": true - }, - { - "color": [ - 255, - 255, - 0 - ], - "selection": { - "c": 3, - "t": 0, - "z": 0 - }, - "slider": [ - 5, - 3318 - ], - "visible": true - } - ], - "colormap": null, - "domainType": "Min/Max", - "index": 0, - "modelMatrix": [ - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1 - ], - "opacity": 1, - "renderingMode": "Additive", - "transparentColor": null, - "type": "bitmask", - "use3d": false, - "visible": true - } - ] - }, - "spatialSegmentationStrokeWidth": { - "A": 1, - "init_A_obsSegmentations_0": 1, - "init_A_obsSegmentations_1": 1, - "init_A_obsSegmentations_2": 1, - "init_A_obsSegmentations_3": 1, - "init_A_obsSegmentations_4": 1 - }, - "spatialSliceX": { - "A": null - }, - "spatialSliceY": { - "A": null - }, - "spatialSliceZ": { - "A": null - }, - "spatialSpotFilled": { - "A": true - }, - "spatialSpotRadius": { - "A": 25 - }, - "spatialSpotStrokeWidth": { - "A": 1 - }, - "spatialTargetC": { - "A": null, - "init_A_image_0": 0, - "init_A_image_1": 1, - "init_A_image_2": 2, - "init_A_obsSegmentations_0": 0, - "init_A_obsSegmentations_1": 1, - "init_A_obsSegmentations_2": 2, - "init_A_obsSegmentations_3": 3, - "init_A_obsSegmentations_4": 4 - }, - "spatialTargetResolution": { - "A": 0, - "init_A_image_0": null - }, - "spatialTargetT": { - "A": null, - "init_A_image_0": 0, - "init_A_obsSegmentations_0": 0 - }, - "spatialTargetX": { - "A": 22069.348625952447 - }, - "spatialTargetY": { - "A": -26851.066191111484 - }, - "spatialTargetZ": { - "A": null, - "B": null, - "init_A_image_0": 0, - "init_A_obsSegmentations_0": 0 - }, - "spatialZoom": { - "A": -4.912917349031568 - }, - "spotLayer": { - "A": null - }, - "tooltipCrosshairsVisible": { - "A": true - }, - "tooltipsVisible": { - "A": true - }, - "volumetricRenderingAlgorithm": { - "A": "additive", - "init_A_image_0": "maximumIntensityProjection" - } - }, - "datasets": [ - { - "files": [ - { - "fileType": "image.ome-tiff", - "options": { - "offsetsUrl": "https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.offsets.json" - }, - "url": "https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.ome.tif" - }, - { - "fileType": "obsSegmentations.ome-tiff", - "options": { - "obsTypesFromChannelNames": true, - "offsetsUrl": "https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.offsets.json" - }, - "url": "https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.ome.tif" - } - ], - "name": "Spraggins", - "uid": "A" - } - ], - "description": "", - "initStrategy": "auto", - "layout": [ - { - "component": "spatialBeta", - "coordinationScopes": { - "additionalObsSets": "A", - "dataset": "A", - "featureHighlight": "A", - "featureSelection": "A", - "featureType": "A", - "featureValueColormap": "A", - "featureValueColormapRange": "A", - "featureValueType": "A", - "fileUid": "A", - "imageChannel": "A", - "imageLayer": "A", - "legendVisible": "A", - "metaCoordinationScopes": [ - "init_A_image_0", - "init_A_obsSegmentations_0", - "init_A_image_0" - ], - "metaCoordinationScopesBy": [ - "init_A_image_0", - "init_A_obsSegmentations_0", - "init_A_image_0" - ], - "moleculeHighlight": "A", - "obsColorEncoding": "A", - "obsFilter": "A", - "obsHighlight": "A", - "obsLabelsType": "A", - "obsSetColor": "A", - "obsSetHighlight": "A", - "obsSetSelection": "A", - "obsType": "A", - "pixelHighlight": "A", - "pointLayer": "A", - "segmentationChannel": "A", - "segmentationLayer": "A", - "spatialAxisFixed": "A", - "spatialChannelColor": "A", - "spatialChannelLabelSize": "A", - "spatialChannelLabelsOrientation": "A", - "spatialChannelLabelsVisible": "A", - "spatialChannelOpacity": "A", - "spatialChannelVisible": "A", - "spatialChannelWindow": "A", - "spatialLayerColor": "A", - "spatialLayerColormap": "A", - "spatialLayerModelMatrix": "A", - "spatialLayerOpacity": "A", - "spatialLayerTransparentColor": "A", - "spatialLayerVisible": "A", - "spatialNeighborhoodLayer": "A", - "spatialOrbitAxis": "A", - "spatialPointLayer": "A", - "spatialRenderingMode": "A", - "spatialRotation": "A", - "spatialRotationOrbit": "A", - "spatialRotationX": "A", - "spatialRotationY": "A", - "spatialRotationZ": "A", - "spatialSegmentationFilled": "A", - "spatialSegmentationStrokeWidth": "A", - "spatialSliceX": "A", - "spatialSliceY": "A", - "spatialSliceZ": "A", - "spatialSpotFilled": "A", - "spatialSpotRadius": "A", - "spatialSpotStrokeWidth": "A", - "spatialTargetC": "A", - "spatialTargetResolution": "A", - "spatialTargetT": "A", - "spatialTargetX": "A", - "spatialTargetY": "A", - "spatialTargetZ": "A", - "spatialZoom": "A", - "spotLayer": "A", - "tooltipCrosshairsVisible": "A", - "tooltipsVisible": "A", - "volumetricRenderingAlgorithm": "A" - }, - "h": 12, - "uid": "A", - "w": 6, - "x": 0, - "y": 0 - }, - { - "component": "layerControllerBeta", - "coordinationScopes": { - "dataset": "A", - "featureSelection": "A", - "featureType": "A", - "featureValueColormap": "A", - "featureValueColormapRange": "A", - "featureValueType": "A", - "fileUid": "A", - "imageChannel": "A", - "imageLayer": "A", - "legendVisible": "A", - "metaCoordinationScopes": [ - "init_A_image_0", - "init_A_obsSegmentations_0", - "init_A_image_0" - ], - "metaCoordinationScopesBy": [ - "init_A_image_0", - "init_A_obsSegmentations_0", - "init_A_image_0" - ], - "obsColorEncoding": "A", - "obsType": "A", - "photometricInterpretation": "A", - "pointLayer": "A", - "segmentationChannel": "A", - "segmentationLayer": "A", - "spatialChannelColor": "A", - "spatialChannelLabelSize": "A", - "spatialChannelLabelsOrientation": "A", - "spatialChannelLabelsVisible": "A", - "spatialChannelOpacity": "A", - "spatialChannelVisible": "A", - "spatialChannelWindow": "A", - "spatialLayerColor": "A", - "spatialLayerColormap": "A", - "spatialLayerModelMatrix": "A", - "spatialLayerOpacity": "A", - "spatialLayerTransparentColor": "A", - "spatialLayerVisible": "A", - "spatialNeighborhoodLayer": "A", - "spatialOrbitAxis": "A", - "spatialPointLayer": "A", - "spatialRenderingMode": "A", - "spatialRotationOrbit": "A", - "spatialRotationX": "A", - "spatialRotationY": "A", - "spatialRotationZ": "A", - "spatialSegmentationFilled": "A", - "spatialSegmentationStrokeWidth": "A", - "spatialSliceX": "A", - "spatialSliceY": "A", - "spatialSliceZ": "A", - "spatialSpotFilled": "A", - "spatialSpotRadius": "A", - "spatialSpotStrokeWidth": "A", - "spatialTargetC": "A", - "spatialTargetResolution": "A", - "spatialTargetT": "A", - "spatialTargetX": "A", - "spatialTargetY": "A", - "spatialTargetZ": "A", - "spatialZoom": "A", - "spotLayer": "A", - "tooltipCrosshairsVisible": "A", - "tooltipsVisible": "A", - "volumetricRenderingAlgorithm": "A" - }, - "h": 12, - "uid": "B", - "w": 6, - "x": 6, - "y": 0 - } - ], - "name": "", - "uid": "new_config_0.9674035035199323", - "version": "1.0.17" - }, - "custom_js_url": "", - "has_host_name": false, - "height": 600, - "invoke_timeout": 30000, - "js_dev_mode": false, - "js_package_version": "3.5.4", - "layout": "IPY_MODEL_800201ad9e7942fbbb6b0a195ef6ec6a", - "plugin_esm": [], - "proxy": false, - "remount_on_uid_change": false, - "store_urls": [], - "theme": "auto", - "uid": "68fc" - } - } - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_modify_config.mo.py b/docs/notebooks/widget_modify_config.mo.py new file mode 100644 index 00000000..3146e2e2 --- /dev/null +++ b/docs/notebooks/widget_modify_config.mo.py @@ -0,0 +1,131 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Programmatic modification of widget configuration + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + OmeTiffWrapper, + MultiImageWrapper, + CoordinationLevel as CL, + ObsSegmentationsOmeTiffWrapper, + ImageOmeTiffWrapper, + get_initial_coordination_scope_prefix, + ) + import random + return ( + CL, + ImageOmeTiffWrapper, + ObsSegmentationsOmeTiffWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + random, + ) + + +@app.cell +def _( + CL, + ImageOmeTiffWrapper, + ObsSegmentationsOmeTiffWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, +): + vc = VitessceConfig(schema_version="1.0.16") + dataset = vc.add_dataset(name='Spraggins').add_object( + ImageOmeTiffWrapper( + img_url="https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.ome.tif", + offsets_url="https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.offsets.json" + ) + ).add_object( + ObsSegmentationsOmeTiffWrapper( + img_url="https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.ome.tif", + offsets_url="https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.offsets.json", + obs_types_from_channel_names=True + ) + ) + + spatial = vc.add_view("spatialBeta", dataset=dataset) + lc = vc.add_view("layerControllerBeta", dataset=dataset) + + vc.link_views_by_dict([spatial, lc], { + "imageLayer": CL([ + { + "photometricInterpretation": "RGB" + } + ]), + }, meta=True, scope_prefix=get_initial_coordination_scope_prefix("A", "image")) + + vc.layout(spatial | lc); + return (vc,) + + +@app.cell +def _(vc): + vw = vc.widget(remount_on_uid_change=False) + vw + return (vw,) + + +@app.cell +def _(vw): + # Inspect the current configuration value. + # This is a dict in the JSON-based format https://vitessce.io/docs/view-config-json/ + vw.config + return + + +@app.cell +def _(random, vw): + # Programatically set a different zoom level and toggle the visibility/color of different segmentation layers: + vw.config = { + **vw.config, + # Need to provide a fresh "uid" value. + # This will tell Vitessce that the contents should be diff-ed against the previous config. + "uid": f"new_config_{random.random()}", + "coordinationSpace": { + # Information about the coordination space can be found at https://vitessce.io/docs/coordination-types/ + **vw.config["coordinationSpace"], + "spatialZoom": { + **vw.config["coordinationSpace"]["spatialZoom"], + "A": -8 + }, + "spatialChannelVisible": { + **vw.config["coordinationSpace"]["spatialChannelVisible"], + "init_A_obsSegmentations_0": True, + "init_A_obsSegmentations_1": False, + "init_A_obsSegmentations_2": False, + "init_A_obsSegmentations_3": False + }, + "spatialChannelColor": { + **vw.config["coordinationSpace"]["spatialChannelColor"], + "init_A_obsSegmentations_0": [255, 0, 0], + } + } + } + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_neuroglancer.ipynb b/docs/notebooks/widget_neuroglancer.ipynb deleted file mode 100644 index 43f1d933..00000000 --- a/docs/notebooks/widget_neuroglancer.ipynb +++ /dev/null @@ -1,239 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example usage of Neuroglancer view" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " ImageOmeTiffWrapper,\n", - " CsvWrapper,\n", - " hconcat,\n", - " vconcat,\n", - " get_initial_coordination_scope_prefix,\n", - " CoordinationLevel as CL\n", - ")\n", - "from os.path import join" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Configure Vitessce" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.17\")\n", - "dataset = vc.add_dataset(name='Meshes').add_object(\n", - " ImageOmeTiffWrapper(\n", - " img_url='https://lsp-public-data.s3.amazonaws.com/yapp-2023-3d-melanoma/Dataset1-LSP13626-invasive-margin.ome.tiff',\n", - " offsets_url='https://lsp-public-data.s3.amazonaws.com/yapp-2023-3d-melanoma/Dataset1-LSP13626-invasive-margin.offsets.json',\n", - " coordination_values={\n", - " \"fileUid\": 'melanoma',\n", - " },\n", - " )\n", - ").add_object(\n", - " CsvWrapper(\n", - " data_type=\"obsEmbedding\",\n", - " csv_url='https://storage.googleapis.com/vitessce-demo-data/neuroglancer-march-2025/melanoma_with_embedding_filtered_ids.csv',\n", - " options= {\n", - " \"obsIndex\": 'id',\n", - " \"obsEmbedding\": ['tSNE1', 'tSNE2'],\n", - " },\n", - " coordination_values= {\n", - " \"obsType\": 'cell',\n", - " \"embeddingType\": 'TSNE',\n", - " },\n", - " )\n", - ").add_object(\n", - " CsvWrapper(\n", - " data_type=\"obsSets\",\n", - " csv_url='https://storage.googleapis.com/vitessce-demo-data/neuroglancer-march-2025/melanoma_with_embedding_filtered_ids.csv',\n", - " coordination_values={\n", - " \"obsType\": 'cell',\n", - " },\n", - " options= {\n", - " \"obsIndex\": 'id',\n", - " \"obsSets\": [\n", - " {\n", - " \"name\": 'Clusters',\n", - " \"column\": 'cluster',\n", - " },\n", - " ],\n", - " },\n", - " )\n", - ")\n", - "spatialThreeView = vc.add_view('spatialBeta', dataset=dataset);\n", - "lcView = vc.add_view('layerControllerBeta', dataset=dataset);\n", - "obsSets = vc.add_view('obsSets', dataset=dataset);\n", - "scatterView = vc.add_view('scatterplot', dataset=dataset, mapping=\"TSNE\");\n", - "# Configuration via props.viewerState is temporary and subject to change.\n", - "neuroglancerView = vc.add_view('neuroglancer', dataset=dataset).set_props(viewerState={\n", - " \"dimensions\": {\n", - " \"x\": [\n", - " 1e-9,\n", - " \"m\"\n", - " ],\n", - " \"y\": [\n", - " 1e-9,\n", - " \"m\"\n", - " ],\n", - " \"z\": [\n", - " 1e-9,\n", - " \"m\"\n", - " ]\n", - " },\n", - " \"position\": [\n", - " 49.5,\n", - " 1000.5,\n", - " 5209.5\n", - " ],\n", - " \"crossSectionScale\": 1,\n", - " \"projectionOrientation\": [\n", - " -0.636204183101654,\n", - " -0.5028395652770996,\n", - " 0.5443811416625977,\n", - " 0.2145828753709793\n", - " ],\n", - " \"projectionScale\": 1024,\n", - " \"layers\": [\n", - " {\n", - " \"type\": \"segmentation\",\n", - " \"source\": \"precomputed://https://vitessce-data-v2.s3.us-east-1.amazonaws.com/data/sorger/invasive_meshes\",\n", - " \"segments\": [\n", - " \"5\"\n", - " ],\n", - " \"segmentColors\": {\n", - " \"5\": \"red\"\n", - " },\n", - " \"name\": \"segmentation\"\n", - " }\n", - " ],\n", - " \"showSlices\": False,\n", - " \"layout\": \"3d\"\n", - "});\n", - "\n", - "vc.link_views([scatterView], ['embeddingObsRadiusMode', 'embeddingObsRadius'], ['manual', 4]);\n", - "\n", - "# Sync the zoom/rotation/pan states\n", - "vc.link_views_by_dict([spatialThreeView, lcView, neuroglancerView], {\n", - " \"spatialRenderingMode\": '3D',\n", - " \"spatialZoom\": 0,\n", - " \"spatialTargetT\": 0,\n", - " \"spatialTargetX\": 0,\n", - " \"spatialTargetY\": 0,\n", - " \"spatialTargetZ\": 0,\n", - " \"spatialRotationX\": 0,\n", - " \"spatialRotationY\": 0,\n", - "}, meta=False);\n", - "\n", - "# Initialize the image properties\n", - "vc.link_views_by_dict([spatialThreeView, lcView], {\n", - " \"imageLayer\": CL([\n", - " {\n", - " \"fileUid\": 'melanoma',\n", - " \"spatialLayerOpacity\": 1,\n", - " \"spatialTargetResolution\": None,\n", - " \"imageChannel\": CL([\n", - " {\n", - " \"spatialTargetC\": 0,\n", - " \"spatialChannelColor\": [255, 0, 0],\n", - " \"spatialChannelVisible\": True,\n", - " \"spatialChannelOpacity\": 1.0,\n", - " },\n", - " ]),\n", - " },\n", - " ]),\n", - "}, scope_prefix=get_initial_coordination_scope_prefix('A', 'image'));\n", - "\n", - "\n", - "vc.layout(hconcat(neuroglancerView, spatialThreeView, vconcat(lcView, obsSets, scatterView)));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Create the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_neuroglancer.mo.py b/docs/notebooks/widget_neuroglancer.mo.py new file mode 100644 index 00000000..bdb4ad4f --- /dev/null +++ b/docs/notebooks/widget_neuroglancer.mo.py @@ -0,0 +1,214 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Example usage of Neuroglancer view + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + ImageOmeTiffWrapper, + CsvWrapper, + hconcat, + vconcat, + get_initial_coordination_scope_prefix, + CoordinationLevel as CL + ) + from os.path import join + return ( + CL, + CsvWrapper, + ImageOmeTiffWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + hconcat, + vconcat, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Configure Vitessce + """ + ) + return + + +@app.cell +def _( + CL, + CsvWrapper, + ImageOmeTiffWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + hconcat, + vconcat, +): + vc = VitessceConfig(schema_version="1.0.17") + dataset = vc.add_dataset(name='Meshes').add_object( + ImageOmeTiffWrapper( + img_url='https://lsp-public-data.s3.amazonaws.com/yapp-2023-3d-melanoma/Dataset1-LSP13626-invasive-margin.ome.tiff', + offsets_url='https://lsp-public-data.s3.amazonaws.com/yapp-2023-3d-melanoma/Dataset1-LSP13626-invasive-margin.offsets.json', + coordination_values={ + "fileUid": 'melanoma', + }, + ) + ).add_object( + CsvWrapper( + data_type="obsEmbedding", + csv_url='https://storage.googleapis.com/vitessce-demo-data/neuroglancer-march-2025/melanoma_with_embedding_filtered_ids.csv', + options= { + "obsIndex": 'id', + "obsEmbedding": ['tSNE1', 'tSNE2'], + }, + coordination_values= { + "obsType": 'cell', + "embeddingType": 'TSNE', + }, + ) + ).add_object( + CsvWrapper( + data_type="obsSets", + csv_url='https://storage.googleapis.com/vitessce-demo-data/neuroglancer-march-2025/melanoma_with_embedding_filtered_ids.csv', + coordination_values={ + "obsType": 'cell', + }, + options= { + "obsIndex": 'id', + "obsSets": [ + { + "name": 'Clusters', + "column": 'cluster', + }, + ], + }, + ) + ) + spatialThreeView = vc.add_view('spatialBeta', dataset=dataset); + lcView = vc.add_view('layerControllerBeta', dataset=dataset); + obsSets = vc.add_view('obsSets', dataset=dataset); + scatterView = vc.add_view('scatterplot', dataset=dataset, mapping="TSNE"); + # Configuration via props.viewerState is temporary and subject to change. + neuroglancerView = vc.add_view('neuroglancer', dataset=dataset).set_props(viewerState={ + "dimensions": { + "x": [ + 1e-9, + "m" + ], + "y": [ + 1e-9, + "m" + ], + "z": [ + 1e-9, + "m" + ] + }, + "position": [ + 49.5, + 1000.5, + 5209.5 + ], + "crossSectionScale": 1, + "projectionOrientation": [ + -0.636204183101654, + -0.5028395652770996, + 0.5443811416625977, + 0.2145828753709793 + ], + "projectionScale": 1024, + "layers": [ + { + "type": "segmentation", + "source": "precomputed://https://vitessce-data-v2.s3.us-east-1.amazonaws.com/data/sorger/invasive_meshes", + "segments": [ + "5" + ], + "segmentColors": { + "5": "red" + }, + "name": "segmentation" + } + ], + "showSlices": False, + "layout": "3d" + }); + + vc.link_views([scatterView], ['embeddingObsRadiusMode', 'embeddingObsRadius'], ['manual', 4]); + + # Sync the zoom/rotation/pan states + vc.link_views_by_dict([spatialThreeView, lcView, neuroglancerView], { + "spatialRenderingMode": '3D', + "spatialZoom": 0, + "spatialTargetT": 0, + "spatialTargetX": 0, + "spatialTargetY": 0, + "spatialTargetZ": 0, + "spatialRotationX": 0, + "spatialRotationY": 0, + }, meta=False); + + # Initialize the image properties + vc.link_views_by_dict([spatialThreeView, lcView], { + "imageLayer": CL([ + { + "fileUid": 'melanoma', + "spatialLayerOpacity": 1, + "spatialTargetResolution": None, + "imageChannel": CL([ + { + "spatialTargetC": 0, + "spatialChannelColor": [255, 0, 0], + "spatialChannelVisible": True, + "spatialChannelOpacity": 1.0, + }, + ]), + }, + ]), + }, scope_prefix=get_initial_coordination_scope_prefix('A', 'image')); + + + vc.layout(hconcat(neuroglancerView, spatialThreeView, vconcat(lcView, obsSets, scatterView))); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Create the Vitessce widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_on_colab.ipynb b/docs/notebooks/widget_on_colab.ipynb deleted file mode 100644 index 309ff7e5..00000000 --- a/docs/notebooks/widget_on_colab.ipynb +++ /dev/null @@ -1,137 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Setup for Google Colab" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Install the package" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!pip install vitessce[all]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of Multi-Modal Imaging Data\n", - "We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " OmeTiffWrapper,\n", - " MultiImageWrapper,\n", - ")\n", - "from os.path import join" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Configure Vitessce\n", - "Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", - "dataset = vc.add_dataset(name='Spraggins').add_object(\n", - " MultiImageWrapper(\n", - " image_wrappers=[\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode')\n", - " ],\n", - " use_physical_size_scaling=True,\n", - " )\n", - ")\n", - "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", - "status = vc.add_view(cm.STATUS, dataset=dataset)\n", - "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True)\n", - "vc.layout(spatial | (lc / status));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Create the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_on_colab.mo.py b/docs/notebooks/widget_on_colab.mo.py new file mode 100644 index 00000000..15b008d3 --- /dev/null +++ b/docs/notebooks/widget_on_colab.mo.py @@ -0,0 +1,115 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Setup for Google Colab + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## Install the package + """ + ) + return + + +app._unparsable_cell( + r""" + !pip install vitessce[all] + """, + name="_" +) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of Multi-Modal Imaging Data + We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal. + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + OmeTiffWrapper, + MultiImageWrapper, + ) + from os.path import join + return MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Configure Vitessce + Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes. + """ + ) + return + + +@app.cell +def _(MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm): + vc = VitessceConfig(schema_version="1.0.15", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca') + dataset = vc.add_dataset(name='Spraggins').add_object( + MultiImageWrapper( + image_wrappers=[ + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode') + ], + use_physical_size_scaling=True, + ) + ) + spatial = vc.add_view(cm.SPATIAL, dataset=dataset) + status = vc.add_view(cm.STATUS, dataset=dataset) + lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True) + vc.layout(spatial | (lc / status)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Create the Vitessce widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_pbmc.ipynb b/docs/notebooks/widget_pbmc.ipynb deleted file mode 100644 index 284546aa..00000000 --- a/docs/notebooks/widget_pbmc.ipynb +++ /dev/null @@ -1,204 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of 3k PBMC reference" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join, isfile, isdir\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")\n", - "from vitessce.data_utils import (\n", - " optimize_adata,\n", - " VAR_CHUNK_SIZE,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Download the dataset\n", - "\n", - "Download `pbmc3k_final.h5ad` from https://seurat.nygenome.org/pbmc3k_final.h5ad" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata_filepath = join(\"data\", \"pbmc3k_final.h5ad\")\n", - "if not isfile(adata_filepath):\n", - " os.makedirs(\"data\", exist_ok=True)\n", - " urlretrieve('https://seurat.nygenome.org/pbmc3k_final.h5ad', adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Load the dataset\n", - "\n", - "Load the dataset using AnnData's `read_h5ad` function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = read_h5ad(adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.1 Save the AnnData object to Zarr" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zarr_filepath = join(\"data\", \"pbmc3k_final.zarr\")\n", - "if not isdir(zarr_filepath):\n", - " adata = optimize_adata(\n", - " adata,\n", - " obs_cols=[\"leiden\"],\n", - " obsm_keys=[\"X_umap\", \"X_pca\"],\n", - " optimize_X=True,\n", - " )\n", - " adata.write_zarr(zarr_filepath, chunks=[adata.shape[0], VAR_CHUNK_SIZE])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Create a Vitessce view config\n", - "\n", - "Define the data and views you would like to include in the widget.\n", - "\n", - "For more details about how to configure data depending on where the files are located relative to the notebook execution, see https://python-docs.vitessce.io/data_options.html." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='PBMC Reference')\n", - "dataset = vc.add_dataset(name='PBMC 3k').add_object(AnnDataWrapper(\n", - " adata_store=zarr_filepath,\n", - " obs_set_paths=[\"obs/leiden\"],\n", - " obs_set_names=[\"Leiden\"],\n", - " obs_embedding_paths=[\"obsm/X_umap\", \"obsm/X_pca\"],\n", - " obs_embedding_names=[\"UMAP\", \"PCA\"],\n", - " obs_feature_matrix_path=\"X\"\n", - "))\n", - "\n", - "umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"PCA\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)\n", - "\n", - "vc.layout((umap / pca) | ((cell_sets | genes) / heatmap));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Create the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.9.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_pbmc.mo.py b/docs/notebooks/widget_pbmc.mo.py new file mode 100644 index 00000000..77b4e711 --- /dev/null +++ b/docs/notebooks/widget_pbmc.mo.py @@ -0,0 +1,180 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of 3k PBMC reference + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join, isfile, isdir + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + from vitessce.data_utils import ( + optimize_adata, + VAR_CHUNK_SIZE, + ) + return ( + AnnDataWrapper, + VAR_CHUNK_SIZE, + VitessceConfig, + cm, + isdir, + isfile, + join, + optimize_adata, + os, + read_h5ad, + urlretrieve, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Download the dataset + + Download `pbmc3k_final.h5ad` from https://seurat.nygenome.org/pbmc3k_final.h5ad + """ + ) + return + + +@app.cell +def _(isfile, join, os, urlretrieve): + adata_filepath = join("data", "pbmc3k_final.h5ad") + if not isfile(adata_filepath): + os.makedirs("data", exist_ok=True) + urlretrieve('https://seurat.nygenome.org/pbmc3k_final.h5ad', adata_filepath) + return (adata_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Load the dataset + + Load the dataset using AnnData's `read_h5ad` function. + """ + ) + return + + +@app.cell +def _(adata_filepath, read_h5ad): + adata = read_h5ad(adata_filepath) + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3.1 Save the AnnData object to Zarr + """ + ) + return + + +@app.cell +def _(VAR_CHUNK_SIZE, adata, isdir, join, optimize_adata): + zarr_filepath = join('data', 'pbmc3k_final.zarr') + if not isdir(zarr_filepath): + adata_1 = optimize_adata(adata, obs_cols=['leiden'], obsm_keys=['X_umap', 'X_pca'], optimize_X=True) + adata_1.write_zarr(zarr_filepath, chunks=[adata_1.shape[0], VAR_CHUNK_SIZE]) + return (zarr_filepath,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Create a Vitessce view config + + Define the data and views you would like to include in the widget. + + For more details about how to configure data depending on where the files are located relative to the notebook execution, see https://python-docs.vitessce.io/data_options.html. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, cm, zarr_filepath): + vc = VitessceConfig(schema_version="1.0.15", name='PBMC Reference') + dataset = vc.add_dataset(name='PBMC 3k').add_object(AnnDataWrapper( + adata_store=zarr_filepath, + obs_set_paths=["obs/leiden"], + obs_set_names=["Leiden"], + obs_embedding_paths=["obsm/X_umap", "obsm/X_pca"], + obs_embedding_names=["UMAP", "PCA"], + obs_feature_matrix_path="X" + )) + + umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="PCA") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + heatmap = vc.add_view(cm.HEATMAP, dataset=dataset) + + vc.layout((umap / pca) | ((cell_sets | genes) / heatmap)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 5. Create the Vitessce widget + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_pbmc_remote.ipynb b/docs/notebooks/widget_pbmc_remote.ipynb deleted file mode 100644 index 5b9101c8..00000000 --- a/docs/notebooks/widget_pbmc_remote.ipynb +++ /dev/null @@ -1,139 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of 3k PBMC reference from Remote Zarr Store" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "We need to import the classes and functions that we will be using from the corresponding packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Set the URL for the Remote Dataset\n", - "\n", - "For this example, we already have uploaded the `pbmc3k` dataset as a zarr store from the [scanpy docs](https://scanpy.readthedocs.io/en/stable/api/scanpy.datasets.pbmc3k.html) to the cloud." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "url = 'https://storage.googleapis.com/vitessce-demo-data/anndata-test/pbmc3k_processed.zarr/'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Create a Vitessce view config\n", - "\n", - "Define the data and views you would like to include in the widget." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='PBMC Reference')\n", - "dataset = vc.add_dataset(name='PBMC 3k').add_object(AnnDataWrapper(adata_url=url, obs_set_paths=[\"obs/louvain\"], obs_set_names=[\"Louvain\"], obs_embedding_paths=[\"obsm/X_umap\", \"obsm/X_pca\"], obs_embedding_names=[\"UMAP\", \"PCA\"], obs_feature_matrix_path=\"X\"))\n", - "\n", - "umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"UMAP\")\n", - "pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping=\"PCA\")\n", - "cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset)\n", - "genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset)\n", - "heatmap = vc.add_view(cm.HEATMAP, dataset=dataset)\n", - "\n", - "vc.layout((umap / pca) | ((cell_sets | genes) / heatmap));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Create the Vitessce widget" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A widget can be created with the `.widget()` method on the config instance. Here, the `proxy=True` parameter allows this widget to be used in a cloud notebook environment, such as Binder." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_pbmc_remote.mo.py b/docs/notebooks/widget_pbmc_remote.mo.py new file mode 100644 index 00000000..81024869 --- /dev/null +++ b/docs/notebooks/widget_pbmc_remote.mo.py @@ -0,0 +1,125 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of 3k PBMC reference from Remote Zarr Store + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + We need to import the classes and functions that we will be using from the corresponding packages. + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + return AnnDataWrapper, VitessceConfig, cm + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Set the URL for the Remote Dataset + + For this example, we already have uploaded the `pbmc3k` dataset as a zarr store from the [scanpy docs](https://scanpy.readthedocs.io/en/stable/api/scanpy.datasets.pbmc3k.html) to the cloud. + """ + ) + return + + +@app.cell +def _(): + url = 'https://storage.googleapis.com/vitessce-demo-data/anndata-test/pbmc3k_processed.zarr/' + return (url,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Create a Vitessce view config + + Define the data and views you would like to include in the widget. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, cm, url): + vc = VitessceConfig(schema_version="1.0.15", name='PBMC Reference') + dataset = vc.add_dataset(name='PBMC 3k').add_object(AnnDataWrapper(adata_url=url, obs_set_paths=["obs/louvain"], obs_set_names=["Louvain"], obs_embedding_paths=["obsm/X_umap", "obsm/X_pca"], obs_embedding_names=["UMAP", "PCA"], obs_feature_matrix_path="X")) + + umap = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="UMAP") + pca = vc.add_view(cm.SCATTERPLOT, dataset=dataset, mapping="PCA") + cell_sets = vc.add_view(cm.OBS_SETS, dataset=dataset) + genes = vc.add_view(cm.FEATURE_LIST, dataset=dataset) + heatmap = vc.add_view(cm.HEATMAP, dataset=dataset) + + vc.layout((umap / pca) | ((cell_sets | genes) / heatmap)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 4. Create the Vitessce widget + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + A widget can be created with the `.widget()` method on the config instance. Here, the `proxy=True` parameter allows this widget to be used in a cloud notebook environment, such as Binder. + """ + ) + return + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_plugin_custom.ipynb b/docs/notebooks/widget_plugin_custom.ipynb deleted file mode 100644 index c8b19f85..00000000 --- a/docs/notebooks/widget_plugin_custom.ipynb +++ /dev/null @@ -1,184 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Vitessce custom plugin definition" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " OmeTiffWrapper,\n", - " MultiImageWrapper,\n", - " VitesscePlugin\n", - ")\n", - "from oxc_py import transform" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "PLUGIN_ESM = transform(\"\"\"\n", - "function createPlugins(utilsForPlugins) {\n", - " const {\n", - " React,\n", - " PluginFileType,\n", - " PluginViewType,\n", - " PluginCoordinationType,\n", - " PluginJointFileType,\n", - " z,\n", - " useCoordination,\n", - " invokeCommand,\n", - " } = utilsForPlugins;\n", - " \n", - " const CSS = `\n", - " .chat {\n", - " overflow-y: scroll;\n", - " }\n", - " `;\n", - " \n", - " function ChatView(props) {\n", - " \n", - " const [nextMessage, setNextMessage] = React.useState('');\n", - " const [isLoading, setIsLoading] = React.useState(false);\n", - " const [chatHistory, setChatHistory] = React.useState([]); // chatHistory is an array of message objects like [{ user, text }, ...]\n", - " \n", - " async function handleClick() { \n", - " setChatHistory(prev => ([\n", - " ...prev,\n", - " { user: 'You', text: nextMessage },\n", - " ]));\n", - " setIsLoading(true);\n", - " const [chatReceiveValue, chatReceiveBuffers] = await invokeCommand(\"chat_send\", nextMessage, []);\n", - " setChatHistory(prev => ([\n", - " ...prev,\n", - " { user: 'AI', text: chatReceiveValue.text },\n", - " ]));\n", - " setIsLoading(false);\n", - " }\n", - " \n", - " return (\n", - " <>\n", - " \n", - "
\n", - "

Chat view

\n", - "
\n", - " {chatHistory.map(message => (\n", - "

\n", - " {message.user}:\n", - " {message.text}\n", - "

\n", - " ))}\n", - "
\n", - " setNextMessage(e.target.value)} disabled={isLoading} />\n", - " \n", - "
\n", - " \n", - " );\n", - " }\n", - "\n", - " const pluginViewTypes = [\n", - " new PluginViewType('chat', ChatView, []),\n", - " ];\n", - " return { pluginViewTypes };\n", - "}\n", - "export default { createPlugins };\n", - "\"\"\")\n", - "\n", - "\n", - "def handle_chat_message(message, buffers):\n", - " return { \"text\": message.upper() }, []\n", - "\n", - "\n", - "class ChatPlugin(VitesscePlugin):\n", - " plugin_esm = PLUGIN_ESM\n", - " commands = {\n", - " \"chat_send\": handle_chat_message,\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", - "dataset = vc.add_dataset(name='Spraggins').add_object(\n", - " MultiImageWrapper(\n", - " image_wrappers=[\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode')\n", - " ],\n", - " use_physical_size_scaling=True,\n", - " )\n", - ")\n", - "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", - "status = vc.add_view(\"chat\", dataset=dataset)\n", - "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True)\n", - "vc.layout(spatial | (lc / status));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Create the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget(plugins=[ChatPlugin()])\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_plugin_custom.mo.py b/docs/notebooks/widget_plugin_custom.mo.py new file mode 100644 index 00000000..4e605257 --- /dev/null +++ b/docs/notebooks/widget_plugin_custom.mo.py @@ -0,0 +1,165 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Vitessce custom plugin definition + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + OmeTiffWrapper, + MultiImageWrapper, + VitesscePlugin + ) + from oxc_py import transform + return ( + MultiImageWrapper, + OmeTiffWrapper, + VitessceConfig, + VitesscePlugin, + cm, + transform, + ) + + +@app.cell +def _(VitesscePlugin, transform): + PLUGIN_ESM = transform(""" + function createPlugins(utilsForPlugins) { + const { + React, + PluginFileType, + PluginViewType, + PluginCoordinationType, + PluginJointFileType, + z, + useCoordination, + invokeCommand, + } = utilsForPlugins; + + const CSS = ` + .chat { + overflow-y: scroll; + } + `; + + function ChatView(props) { + + const [nextMessage, setNextMessage] = React.useState(''); + const [isLoading, setIsLoading] = React.useState(false); + const [chatHistory, setChatHistory] = React.useState([]); // chatHistory is an array of message objects like [{ user, text }, ...] + + async function handleClick() { + setChatHistory(prev => ([ + ...prev, + { user: 'You', text: nextMessage }, + ])); + setIsLoading(true); + const [chatReceiveValue, chatReceiveBuffers] = await invokeCommand("chat_send", nextMessage, []); + setChatHistory(prev => ([ + ...prev, + { user: 'AI', text: chatReceiveValue.text }, + ])); + setIsLoading(false); + } + + return ( + <> + +
+

Chat view

+
+ {chatHistory.map(message => ( +

+ {message.user}: + {message.text} +

+ ))} +
+ setNextMessage(e.target.value)} disabled={isLoading} /> + +
+ + ); + } + + const pluginViewTypes = [ + new PluginViewType('chat', ChatView, []), + ]; + return { pluginViewTypes }; + } + export default { createPlugins }; + """) + + + def handle_chat_message(message, buffers): + return { "text": message.upper() }, [] + + + class ChatPlugin(VitesscePlugin): + plugin_esm = PLUGIN_ESM + commands = { + "chat_send": handle_chat_message, + } + return (ChatPlugin,) + + +@app.cell +def _(MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm): + vc = VitessceConfig(schema_version="1.0.15", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca') + dataset = vc.add_dataset(name='Spraggins').add_object( + MultiImageWrapper( + image_wrappers=[ + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode') + ], + use_physical_size_scaling=True, + ) + ) + spatial = vc.add_view(cm.SPATIAL, dataset=dataset) + status = vc.add_view("chat", dataset=dataset) + lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True) + vc.layout(spatial | (lc / status)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Create the Vitessce widget + """ + ) + return + + +@app.cell +def _(ChatPlugin, vc): + vw = vc.widget(plugins=[ChatPlugin()]) + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_plugin_demo.ipynb b/docs/notebooks/widget_plugin_demo.ipynb deleted file mode 100644 index ca25fe1c..00000000 --- a/docs/notebooks/widget_plugin_demo.ipynb +++ /dev/null @@ -1,122 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Vitessce plugin usage demo" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " OmeTiffWrapper,\n", - " MultiImageWrapper,\n", - ")\n", - "from os.path import join" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce.widget_plugins import DemoPlugin" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Configure Vitessce\n", - "Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.15\", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca')\n", - "dataset = vc.add_dataset(name='Spraggins').add_object(\n", - " MultiImageWrapper(\n", - " image_wrappers=[\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'),\n", - " OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode')\n", - " ],\n", - " use_physical_size_scaling=True,\n", - " )\n", - ")\n", - "spatial = vc.add_view(cm.SPATIAL, dataset=dataset)\n", - "status = vc.add_view(\"demo\", dataset=dataset)\n", - "lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True)\n", - "vc.layout(spatial | (lc / status));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Create the Vitessce widget" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget(plugins=[DemoPlugin()])\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_plugin_demo.mo.py b/docs/notebooks/widget_plugin_demo.mo.py new file mode 100644 index 00000000..be7de441 --- /dev/null +++ b/docs/notebooks/widget_plugin_demo.mo.py @@ -0,0 +1,102 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Vitessce plugin usage demo + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + We visualize IMS, PAS, and AF imaging data overlaid from the Spraggins Lab of the Biomolecular Multimodal Imaging Center (BIOMC) at Vanderbilt University, uploaded to the HuBMAP data portal. + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + OmeTiffWrapper, + MultiImageWrapper, + ) + from os.path import join + return MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm + + +@app.cell +def _(): + from vitessce.widget_plugins import DemoPlugin + return (DemoPlugin,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Configure Vitessce + Set up the images from the three different assays, with the `use_physical_size_scaling` set to `True` so that the IMS image scales to the other images based on their physical sizes. + """ + ) + return + + +@app.cell +def _(MultiImageWrapper, OmeTiffWrapper, VitessceConfig, cm): + vc = VitessceConfig(schema_version="1.0.15", name='Spraggins Multi-Modal', description='PAS + IMS + AF From https://portal.hubmapconsortium.org/browse/collection/6a6efd0c1a2681dc7d2faab8e4ab0bca') + dataset = vc.add_dataset(name='Spraggins').add_object( + MultiImageWrapper( + image_wrappers=[ + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/f4188a148e4c759092d19369d310883b/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-PAS_images/VAN0006-LK-2-85-PAS_registered.ome.tif?token=', name='PAS'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/2130d5f91ce61d7157a42c0497b06de8/ometiff-pyramids/processedMicroscopy/VAN0006-LK-2-85-AF_preIMS_images/VAN0006-LK-2-85-AF_preIMS_registered.ome.tif?token=', name='AF'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/be503a021ed910c0918842e318e6efa2/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_PosMode_multilayer.ome.tif?token=', name='IMS Pos Mode'), + OmeTiffWrapper(img_url='https://assets.hubmapconsortium.org/ca886a630b2038997a4cfbbf4abfd283/ometiff-pyramids/ometiffs/VAN0006-LK-2-85-IMS_NegMode_multilayer.ome.tif?token=', name='IMS Neg Mode') + ], + use_physical_size_scaling=True, + ) + ) + spatial = vc.add_view(cm.SPATIAL, dataset=dataset) + status = vc.add_view("demo", dataset=dataset) + lc = vc.add_view(cm.LAYER_CONTROLLER, dataset=dataset).set_props(disableChannelsIfRgbDetected=True) + vc.layout(spatial | (lc / status)); + return (vc,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Create the Vitessce widget + """ + ) + return + + +@app.cell +def _(DemoPlugin, vc): + vw = vc.widget(plugins=[DemoPlugin()]) + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_plugin_spatial-query.ipynb b/docs/notebooks/widget_plugin_spatial-query.ipynb deleted file mode 100644 index c204f834..00000000 --- a/docs/notebooks/widget_plugin_spatial-query.ipynb +++ /dev/null @@ -1,152 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Vitessce SpatialQuery plugin usage demo" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "#!pip install \"vitessce[all]==3.3.0\" esbuild_py anndata\n", - "!pip install \"mlxtend~=0.23.0\"\n", - "#!pip install -i \"https://test.pypi.org/simple/\" SpatialQuery\n", - "!pip install \"SpatialQuery @ git+https://github.com/ShaokunAn/Spatial-Query@main\"" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from os.path import join\n", - "from anndata import read_h5ad\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " AnnDataWrapper,\n", - " ViewType as vt,\n", - " CoordinationType as ct,\n", - " CoordinationLevel as CL,\n", - ")\n", - "from vitessce.widget_plugins import SpatialQueryPlugin" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "adata = read_h5ad(join(\"data\", \"HBM987_KWLK_254\", \"secondary_analysis.h5ad\"))\n", - "zarr_path = join(\"data\", \"HBM987_KWLK_254\", \"secondary_analysis.h5ad.zarr\")\n", - "adata.write_zarr(zarr_path)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "plugin = SpatialQueryPlugin(adata)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.16\", name=\"Spatial-Query\")\n", - "dataset = vc.add_dataset(\"Query results\").add_object(AnnDataWrapper(\n", - " adata_path=zarr_path,\n", - " obs_feature_matrix_path=\"X\",\n", - " obs_set_paths=[\"obs/predicted.ASCT.celltype\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_spots_path=\"obsm/X_spatial\",\n", - " feature_labels_path=\"var/hugo_symbol\",\n", - " coordination_values={\n", - " \"featureLabelsType\": \"Gene symbol\",\n", - " }\n", - "))\n", - "\n", - "spatial_view = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "lc_view = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "sets_view = vc.add_view(\"obsSets\", dataset=dataset)\n", - "features_view = vc.add_view(\"featureList\", dataset=dataset)\n", - "sq_view = vc.add_view(\"spatialQuery\", dataset=dataset)\n", - "\n", - "obs_set_selection_scope, = vc.add_coordination(\"obsSetSelection\",)\n", - "obs_set_selection_scope.set_value(None)\n", - "\n", - "sets_view.use_coordination(obs_set_selection_scope)\n", - "sq_view.use_coordination(obs_set_selection_scope)\n", - "spatial_view.use_coordination(obs_set_selection_scope)\n", - "features_view.use_coordination(obs_set_selection_scope)\n", - "\n", - "vc.link_views([spatial_view, lc_view, sets_view, features_view],\n", - " [\"additionalObsSets\", \"obsSetColor\"],\n", - " [plugin.additional_obs_sets, plugin.obs_set_color]\n", - ")\n", - "vc.link_views_by_dict([spatial_view, lc_view], {\n", - " \"spotLayer\": CL([\n", - " {\n", - " \"obsType\": \"cell\",\n", - " \"spatialSpotRadius\": 15,\n", - " },\n", - " ])\n", - "})\n", - "\n", - "vc.layout((spatial_view | (lc_view / features_view)) / (sets_view | sq_view));" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget(height=900, plugins=[plugin], remount_on_uid_change=False)\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_plugin_spatial-query.mo.py b/docs/notebooks/widget_plugin_spatial-query.mo.py new file mode 100644 index 00000000..a54feca0 --- /dev/null +++ b/docs/notebooks/widget_plugin_spatial-query.mo.py @@ -0,0 +1,124 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Vitessce SpatialQuery plugin usage demo + """ + ) + return + + +app._unparsable_cell( + r""" + #!pip install \"vitessce[all]==3.3.0\" esbuild_py anndata + !pip install \"mlxtend~=0.23.0\" + #!pip install -i \"https://test.pypi.org/simple/\" SpatialQuery + !pip install \"SpatialQuery @ git+https://github.com/ShaokunAn/Spatial-Query@main\" + """, + name="_" +) + + +@app.cell +def _(): + from os.path import join + from anndata import read_h5ad + from vitessce import ( + VitessceConfig, + AnnDataWrapper, + ViewType as vt, + CoordinationType as ct, + CoordinationLevel as CL, + ) + from vitessce.widget_plugins import SpatialQueryPlugin + return ( + AnnDataWrapper, + CL, + SpatialQueryPlugin, + VitessceConfig, + join, + read_h5ad, + ) + + +@app.cell +def _(join, read_h5ad): + adata = read_h5ad(join("data", "HBM987_KWLK_254", "secondary_analysis.h5ad")) + zarr_path = join("data", "HBM987_KWLK_254", "secondary_analysis.h5ad.zarr") + adata.write_zarr(zarr_path) + return adata, zarr_path + + +@app.cell +def _(SpatialQueryPlugin, adata): + plugin = SpatialQueryPlugin(adata) + return (plugin,) + + +@app.cell +def _(AnnDataWrapper, CL, VitessceConfig, plugin, zarr_path): + vc = VitessceConfig(schema_version="1.0.16", name="Spatial-Query") + dataset = vc.add_dataset("Query results").add_object(AnnDataWrapper( + adata_path=zarr_path, + obs_feature_matrix_path="X", + obs_set_paths=["obs/predicted.ASCT.celltype"], + obs_set_names=["Cell Type"], + obs_spots_path="obsm/X_spatial", + feature_labels_path="var/hugo_symbol", + coordination_values={ + "featureLabelsType": "Gene symbol", + } + )) + + spatial_view = vc.add_view("spatialBeta", dataset=dataset) + lc_view = vc.add_view("layerControllerBeta", dataset=dataset) + sets_view = vc.add_view("obsSets", dataset=dataset) + features_view = vc.add_view("featureList", dataset=dataset) + sq_view = vc.add_view("spatialQuery", dataset=dataset) + + obs_set_selection_scope, = vc.add_coordination("obsSetSelection",) + obs_set_selection_scope.set_value(None) + + sets_view.use_coordination(obs_set_selection_scope) + sq_view.use_coordination(obs_set_selection_scope) + spatial_view.use_coordination(obs_set_selection_scope) + features_view.use_coordination(obs_set_selection_scope) + + vc.link_views([spatial_view, lc_view, sets_view, features_view], + ["additionalObsSets", "obsSetColor"], + [plugin.additional_obs_sets, plugin.obs_set_color] + ) + vc.link_views_by_dict([spatial_view, lc_view], { + "spotLayer": CL([ + { + "obsType": "cell", + "spatialSpotRadius": 15, + }, + ]) + }) + + vc.layout((spatial_view | (lc_view / features_view)) / (sets_view | sq_view)); + return (vc,) + + +@app.cell +def _(plugin, vc): + vw = vc.widget(height=900, plugins=[plugin], remount_on_uid_change=False) + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_segmentations_beta.ipynb b/docs/notebooks/widget_segmentations_beta.ipynb deleted file mode 100644 index d398414f..00000000 --- a/docs/notebooks/widget_segmentations_beta.ipynb +++ /dev/null @@ -1,104 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization of OME-TIFF images and segmentation masks" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " OmeTiffWrapper,\n", - " MultiImageWrapper,\n", - " CoordinationLevel as CL,\n", - " ObsSegmentationsOmeTiffWrapper,\n", - " ImageOmeTiffWrapper,\n", - " get_initial_coordination_scope_prefix,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vc = VitessceConfig(schema_version=\"1.0.16\")\n", - "dataset = vc.add_dataset(name='Spraggins').add_object(\n", - " ImageOmeTiffWrapper(\n", - " img_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.ome.tif\",\n", - " offsets_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.offsets.json\"\n", - " )\n", - ").add_object(\n", - " ObsSegmentationsOmeTiffWrapper(\n", - " img_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.ome.tif\",\n", - " offsets_url=\"https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.offsets.json\",\n", - " obs_types_from_channel_names=True\n", - " )\n", - ")\n", - "\n", - "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", - "lc = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n", - "\n", - "vc.link_views_by_dict([spatial, lc], {\n", - " \"imageLayer\": CL([\n", - " {\n", - " \"photometricInterpretation\": \"RGB\" \n", - " }\n", - " ]),\n", - "}, meta=True, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"image\"))\n", - "\n", - "vc.layout(spatial | lc);" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = vc.widget()\n", - "vw" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_segmentations_beta.mo.py b/docs/notebooks/widget_segmentations_beta.mo.py new file mode 100644 index 00000000..93d45102 --- /dev/null +++ b/docs/notebooks/widget_segmentations_beta.mo.py @@ -0,0 +1,90 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # Visualization of OME-TIFF images and segmentation masks + """ + ) + return + + +@app.cell +def _(): + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + OmeTiffWrapper, + MultiImageWrapper, + CoordinationLevel as CL, + ObsSegmentationsOmeTiffWrapper, + ImageOmeTiffWrapper, + get_initial_coordination_scope_prefix, + ) + return ( + CL, + ImageOmeTiffWrapper, + ObsSegmentationsOmeTiffWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, + ) + + +@app.cell +def _( + CL, + ImageOmeTiffWrapper, + ObsSegmentationsOmeTiffWrapper, + VitessceConfig, + get_initial_coordination_scope_prefix, +): + vc = VitessceConfig(schema_version="1.0.16") + dataset = vc.add_dataset(name='Spraggins').add_object( + ImageOmeTiffWrapper( + img_url="https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.ome.tif", + offsets_url="https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2_bf.offsets.json" + ) + ).add_object( + ObsSegmentationsOmeTiffWrapper( + img_url="https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.ome.tif", + offsets_url="https://storage.googleapis.com/vitessce-demo-data/kpmp-f2f-march-2023/S-1905-017737/S-1905-017737_PAS_2of2.offsets.json", + obs_types_from_channel_names=True + ) + ) + + spatial = vc.add_view("spatialBeta", dataset=dataset) + lc = vc.add_view("layerControllerBeta", dataset=dataset) + + vc.link_views_by_dict([spatial, lc], { + "imageLayer": CL([ + { + "photometricInterpretation": "RGB" + } + ]), + }, meta=True, scope_prefix=get_initial_coordination_scope_prefix("A", "image")) + + vc.layout(spatial | lc); + return (vc,) + + +@app.cell +def _(vc): + vw = vc.widget() + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/notebooks/widget_shortcut.ipynb b/docs/notebooks/widget_shortcut.ipynb deleted file mode 100644 index 75f758d3..00000000 --- a/docs/notebooks/widget_shortcut.ipynb +++ /dev/null @@ -1,176 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "nbsphinx": "hidden" - }, - "source": [ - "# Vitessce Widget Tutorial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# The from_object shortcut" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Import dependencies\n", - "\n", - "Import the functions and classes that we will be using." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from os.path import join\n", - "from urllib.request import urlretrieve\n", - "from anndata import read_h5ad\n", - "import scanpy as sc\n", - "\n", - "from vitessce import (\n", - " VitessceConfig,\n", - " Component as cm,\n", - " CoordinationType as ct,\n", - " AnnDataWrapper,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Download the data\n", - "\n", - "For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "os.makedirs(\"data\", exist_ok=True)\n", - "adata_filepath = join(\"data\", \"habib17.processed.h5ad\")\n", - "urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Load the data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adata = read_h5ad(join(\"data\", \"habib17.processed.h5ad\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.1. Preprocess the Data For Visualization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "top_dispersion = adata.var[\"dispersions_norm\"][\n", - " sorted(\n", - " range(len(adata.var[\"dispersions_norm\"])),\n", - " key=lambda k: adata.var[\"dispersions_norm\"][k],\n", - " )[-51:][0]\n", - "]\n", - "adata.var[\"top_highly_variable\"] = (\n", - " adata.var[\"dispersions_norm\"] > top_dispersion\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With one line of code, you may create a Vitessce widget based on an automatically inferred configuration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vw = VitessceConfig.from_object(AnnDataWrapper(\n", - " adata,\n", - " obs_embedding_paths=[\"obsm/X_umap\"],\n", - " obs_embedding_names=[\"UMAP\"],\n", - " obs_set_paths=[\"obs/CellType\"],\n", - " obs_set_names=[\"Cell Type\"],\n", - " obs_feature_matrix_path=\"X\",\n", - " feature_filter_path=\"var/top_highly_variable\"\n", - "), schema_version=\"1.0.15\").widget(height=800)\n", - "vw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "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.8.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/notebooks/widget_shortcut.mo.py b/docs/notebooks/widget_shortcut.mo.py new file mode 100644 index 00000000..1ea334ea --- /dev/null +++ b/docs/notebooks/widget_shortcut.mo.py @@ -0,0 +1,138 @@ +import marimo + +__generated_with = "0.13.15" +app = marimo.App() + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + # The from_object shortcut + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 1. Import dependencies + + Import the functions and classes that we will be using. + """ + ) + return + + +@app.cell +def _(): + import os + from os.path import join + from urllib.request import urlretrieve + from anndata import read_h5ad + import scanpy as sc + + from vitessce import ( + VitessceConfig, + Component as cm, + CoordinationType as ct, + AnnDataWrapper, + ) + return AnnDataWrapper, VitessceConfig, join, os, read_h5ad, urlretrieve + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 2. Download the data + + For this example, we need to download a dataset from the COVID-19 Cell Atlas https://www.covid19cellatlas.org/index.healthy.html#habib17. + """ + ) + return + + +@app.cell +def _(join, os, urlretrieve): + os.makedirs("data", exist_ok=True) + adata_filepath = join("data", "habib17.processed.h5ad") + urlretrieve('https://covid19.cog.sanger.ac.uk/habib17.processed.h5ad', adata_filepath) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3. Load the data + """ + ) + return + + +@app.cell +def _(join, read_h5ad): + adata = read_h5ad(join("data", "habib17.processed.h5ad")) + return (adata,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ## 3.1. Preprocess the Data For Visualization + """ + ) + return + + +@app.cell +def _(adata): + top_dispersion = adata.var["dispersions_norm"][ + sorted( + range(len(adata.var["dispersions_norm"])), + key=lambda k: adata.var["dispersions_norm"][k], + )[-51:][0] + ] + adata.var["top_highly_variable"] = ( + adata.var["dispersions_norm"] > top_dispersion + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + With one line of code, you may create a Vitessce widget based on an automatically inferred configuration. + """ + ) + return + + +@app.cell +def _(AnnDataWrapper, VitessceConfig, adata): + vw = VitessceConfig.from_object(AnnDataWrapper( + adata, + obs_embedding_paths=["obsm/X_umap"], + obs_embedding_names=["UMAP"], + obs_set_paths=["obs/CellType"], + obs_set_names=["Cell Type"], + obs_feature_matrix_path="X", + feature_filter_path="var/top_highly_variable" + ), schema_version="1.0.15").widget(height=800) + vw + return + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +if __name__ == "__main__": + app.run() diff --git a/docs/widget_examples.rst b/docs/widget_examples.rst index 4e81237c..2a94cbef 100644 --- a/docs/widget_examples.rst +++ b/docs/widget_examples.rst @@ -4,23 +4,23 @@ Widget examples .. toctree:: :maxdepth: 1 - notebooks/widget_brain - notebooks/widget_brain_h5ad - notebooks/widget_brain_with_base_dir - notebooks/web_app_brain - notebooks/widget_genomic_profiles - notebooks/widget_imaging - notebooks/widget_segmentations_beta - notebooks/widget_pbmc - notebooks/widget_pbmc_remote - notebooks/spatial_data - notebooks/spatial_data_blobs - notebooks/spatial_data_mouseliver - notebooks/spatial_data_mouseliver_remote - notebooks/widget_loom - notebooks/widget_from_dict - notebooks/widget_modify_config - notebooks/widget_plugin_demo - notebooks/widget_plugin_custom - notebooks/widget_plugin_spatial-query - notebooks/page_mode_example + notebooks/__ipynb__/widget_brain + notebooks/__ipynb__/widget_brain_h5ad + notebooks/__ipynb__/widget_brain_with_base_dir + notebooks/__ipynb__/web_app_brain + notebooks/__ipynb__/widget_genomic_profiles + notebooks/__ipynb__/widget_imaging + notebooks/__ipynb__/widget_segmentations_beta + notebooks/__ipynb__/widget_pbmc + notebooks/__ipynb__/widget_pbmc_remote + notebooks/__ipynb__/spatial_data + notebooks/__ipynb__/spatial_data_blobs + notebooks/__ipynb__/spatial_data_mouseliver + notebooks/__ipynb__/spatial_data_mouseliver_remote + notebooks/__ipynb__/widget_loom + notebooks/__ipynb__/widget_from_dict + notebooks/__ipynb__/widget_modify_config + notebooks/__ipynb__/widget_plugin_demo + notebooks/__ipynb__/widget_plugin_custom + notebooks/__ipynb__/widget_plugin_spatial-query + notebooks/__ipynb__/page_mode_example diff --git a/pyproject.toml b/pyproject.toml index a8f51a19..a971207c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,7 +90,7 @@ linting = [] notebook = [ 'spatialdata>=0.3.0', 'dask[dataframe]==2024.11.1', - 'marimo', + 'marimo>=0.14.16', 'starlette>=0.42.0', 'tqdm>=4.1.0', ] diff --git a/scripts/convert_mo_to_ipynb.sh b/scripts/convert_mo_to_ipynb.sh new file mode 100644 index 00000000..72cf47ec --- /dev/null +++ b/scripts/convert_mo_to_ipynb.sh @@ -0,0 +1,15 @@ +cd docs/notebooks + +for file in *.mo.py; do + echo "Converting $file to ${file%.mo.py}.ipynb" + uv run marimo export ipynb "$file" --output "__ipynb__/${file%.mo.py}.ipynb" —-sort top-down --force +done + +# Uncomment the following lines if you want to use the list of existing .ipynb files in __ipynb__ directory for conversion +#for file in __ipynb__/*.ipynb; do +# FILENAME=$(basename "${file%.ipynb}") +# echo "Converting ${FILENAME}.mo.py to ${FILENAME}.ipynb" +# uv run marimo export ipynb "${FILENAME}.mo.py" --output "__ipynb__/${FILENAME}.ipynb" --sort top-down --force +#done + +cp example_configs.py "__ipynb__/example_configs.py" \ No newline at end of file diff --git a/tests-widget/example.spec.js b/tests-widget/example.spec.js index 6f165468..2cc26faa 100644 --- a/tests-widget/example.spec.js +++ b/tests-widget/example.spec.js @@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test'; test('Renders Vitessce widget containing a scatterplot view (Jupyter)', async ({ page }) => { test.setTimeout(60_000); - await page.goto('http://localhost:3000/widget_from_dict.html'); + await page.goto('http://localhost:3000/__ipynb__/widget_from_dict.html'); // Expect a title "to contain" a substring. await expect(page).toHaveTitle('widget_from_dict'); @@ -14,10 +14,10 @@ test('Renders Vitessce widget containing a scatterplot view (Jupyter)', async ({ test('Renders Vitessce widget containing a scatterplot view (Marimo)', async ({ page }) => { test.setTimeout(60_000); - await page.goto('http://localhost:3000/marimo.html'); + await page.goto('http://localhost:3000/marimo.mo.html'); // Expect a title "to contain" a substring. - await expect(page).toHaveTitle('marimo'); + await expect(page).toHaveTitle('marimo.mo'); await expect(page.getByText('Scatterplot (UMAP)')).toBeVisible(); await expect(page.getByText('523 cells')).toHaveCount(3);