Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 251 additions & 0 deletions docs/notebooks/widget_brain_with_quality_metric.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
{
"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
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "vitessce"
version = "3.6.2"
version = "3.6.3"
authors = [
{ name="Mark Keller", email="mark_keller@hms.harvard.edu" },
]
Expand Down
11 changes: 11 additions & 0 deletions src/vitessce/file_def_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ def gen_obs_labels_schema(options: dict, paths: Optional[list[str]] = None, name
return options


def gen_obs_feature_columns_schema(options: dict, obs_feature_column_paths: Optional[list[str]] = None):
if obs_feature_column_paths is not None:
options["obsFeatureColumns"] = [
{
"path": col_path
}
for col_path in obs_feature_column_paths
]
return options


def gen_path_schema(key: str, path: Optional[str], options: dict):
if path is not None:
options[key] = {
Expand Down
6 changes: 3 additions & 3 deletions src/vitessce/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ class VitessceWidget(anywidget.AnyWidget):

next_port = DEFAULT_PORT

js_package_version = Unicode('3.6.3').tag(sync=True)
js_package_version = Unicode('3.6.4').tag(sync=True)
js_dev_mode = Bool(False).tag(sync=True)
custom_js_url = Unicode('').tag(sync=True)
plugin_esm = List(trait=Unicode(''), default_value=[]).tag(sync=True)
Expand All @@ -614,7 +614,7 @@ class VitessceWidget(anywidget.AnyWidget):

store_urls = List(trait=Unicode(''), default_value=[]).tag(sync=True)

def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.6.3', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, prefer_local=True, invoke_timeout=300000, invoke_batched=True, page_mode=False, page_esm=None, prevent_scroll=True):
def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.6.4', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, prefer_local=True, invoke_timeout=300000, invoke_batched=True, page_mode=False, page_esm=None, prevent_scroll=True):
"""
Construct a new Vitessce widget.

Expand Down Expand Up @@ -750,7 +750,7 @@ def _plugin_command(self, params, buffers):
# Launch Vitessce using plain HTML representation (no ipywidgets)


def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.6.3', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, page_mode=False, page_esm=None):
def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.6.4', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, page_mode=False, page_esm=None):
from IPython.display import display, HTML
uid_str = "vitessce" + get_uid_str(uid)

Expand Down
6 changes: 5 additions & 1 deletion src/vitessce/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
gen_sdata_obs_spots_schema,
gen_sdata_obs_sets_schema,
gen_sdata_obs_feature_matrix_schema,
gen_obs_feature_columns_schema,
)

from .constants import (
Expand Down Expand Up @@ -1192,7 +1193,7 @@ def raise_error_if_more_than_one(inputs):


class AnnDataWrapper(AbstractWrapper):
def __init__(self, adata_path=None, adata_url=None, adata_store=None, adata_artifact=None, ref_path=None, ref_url=None, ref_artifact=None, obs_feature_matrix_path=None, feature_filter_path=None, initial_feature_filter_path=None, obs_set_paths=None, obs_set_names=None, obs_locations_path=None, obs_segmentations_path=None, obs_embedding_paths=None, obs_embedding_names=None, obs_embedding_dims=None, obs_spots_path=None, obs_points_path=None, feature_labels_path=None, obs_labels_path=None, convert_to_dense=True, coordination_values=None, obs_labels_paths=None, obs_labels_names=None, is_zip=None, **kwargs):
def __init__(self, adata_path=None, adata_url=None, adata_store=None, adata_artifact=None, ref_path=None, ref_url=None, ref_artifact=None, obs_feature_matrix_path=None, obs_feature_column_paths=None, feature_filter_path=None, initial_feature_filter_path=None, obs_set_paths=None, obs_set_names=None, obs_locations_path=None, obs_segmentations_path=None, obs_embedding_paths=None, obs_embedding_names=None, obs_embedding_dims=None, obs_spots_path=None, obs_points_path=None, feature_labels_path=None, obs_labels_path=None, convert_to_dense=True, coordination_values=None, obs_labels_paths=None, obs_labels_names=None, is_zip=None, **kwargs):
"""
Wrap an AnnData object by creating an instance of the ``AnnDataWrapper`` class.

Expand All @@ -1218,6 +1219,7 @@ def __init__(self, adata_path=None, adata_url=None, adata_store=None, adata_arti
:param str obs_labels_path: (DEPRECATED) The name of a column containing observation labels (e.g., alternate cell IDs), instead of the default index in `obs` of the AnnData store. Use `obs_labels_paths` and `obs_labels_names` instead. This arg will be removed in a future release.
:param list[str] obs_labels_paths: The names of columns containing observation labels (e.g., alternate cell IDs), instead of the default index in `obs` of the AnnData store.
:param list[str] obs_labels_names: The optional display names of columns containing observation labels (e.g., alternate cell IDs), instead of the default index in `obs` of the AnnData store.
:param list[str] obs_feature_column_paths: The paths to columns (typically in `obs`) that contain numerical values per observation (e.g., cell size, quality control metrics, etc.) which are not part of the main expression matrix.
:param bool convert_to_dense: Whether or not to convert `X` to dense the zarr store (dense is faster but takes more disk space).
:param coordination_values: Coordination values for the file definition.
:param is_zip: Boolean indicating whether the Zarr store is in a zipped format.
Expand Down Expand Up @@ -1289,6 +1291,7 @@ def __init__(self, adata_path=None, adata_url=None, adata_store=None, adata_arti
self._spatial_spots_obsm = obs_spots_path
self._spatial_points_obsm = obs_points_path
self._feature_labels = feature_labels_path
self._obs_feature_column_paths = obs_feature_column_paths
# Support legacy provision of single obs labels path
if (obs_labels_path is not None):
warnings.warn("`obs_labels_path` will be deprecated in a future release.", DeprecationWarning)
Expand Down Expand Up @@ -1357,6 +1360,7 @@ def get_anndata_zarr(base_url):
options = gen_obs_feature_matrix_schema(options, self._expression_matrix, self._gene_var_filter, self._matrix_gene_var_filter)
options = gen_feature_labels_schema(self._feature_labels, options)
options = gen_obs_labels_schema(options, self._obs_labels_elems, self._obs_labels_names)
options = gen_obs_feature_columns_schema(options, self._obs_feature_column_paths)

if len(options.keys()) > 0:
if self.is_h5ad:
Expand Down