From d62b6565e1456acd7b59447f82bd5bac994cafcf Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Tue, 20 Jan 2026 16:07:19 -0500 Subject: [PATCH 1/7] WIP: points improvements and multi-scale image fix --- pyproject.toml | 4 +- src/easy_vitessce/spatialdata_plot.py | 128 +++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 45bc711..c8b0b9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "easy_vitessce" -version = "0.0.9" +version = "0.0.10" authors = [ {name="HIDIVE Lab"}, {name="Selena Luo"} @@ -24,7 +24,7 @@ dependencies = [ 'numpy>=1.21.2', 'zarr>=2.5.0,<3', 'numcodecs>=0.5.7,<0.16.0', - 'vitessce[all]>=3.7.9', + 'vitessce[all]>=3.8.0', 'scanpy>=1.11.3', 'anndata>=0.11.4', 'spatialdata>=0.3.0', diff --git a/src/easy_vitessce/spatialdata_plot.py b/src/easy_vitessce/spatialdata_plot.py index 375a269..3199343 100644 --- a/src/easy_vitessce/spatialdata_plot.py +++ b/src/easy_vitessce/spatialdata_plot.py @@ -20,15 +20,15 @@ vconcat, ) -from os.path import join - from spatialdata_plot.pl.basic import PlotAccessor from spatialdata import get_element_annotators -from easy_vitessce.widget import _to_widget, config +from easy_vitessce.widget import _to_widget from easy_vitessce.colors import to_uint8_rgb from easy_vitessce.data import _get_sdata_wrapper_params +from matplotlib.colors import is_color_like + # Internal function for shared logic between render_shapes and render_labels. def _shared_render_shapes_and_labels( sdata, element, table_name, table_layer, color, cmap, norm, groups, palette, obs_type, feature_type, is_spots, fill_alpha, outline_alpha, outline_width, outline_color, @@ -341,12 +341,16 @@ def render_images( # RGB vs. non-RGB logic in spatialdata-plot: # Reference: https://github.com/scverse/spatialdata-plot/blob/010560f7eebdd245693a8c55eede0f895a636f5c/src/spatialdata_plot/pl/render.py#L865 img = self.sdata.images[element] - try: - all_channels = img.coords["c"].values.tolist() - except KeyError: - # TODO: use a better way than try/except of determining whether this is a multi-resolution image. - all_channels = img.scale0.coords["c"].values.tolist() - img_dtype = img.dtype + if hasattr(img, "dtype"): + img_arr = img + else: + # Assume multi-scale (DataTree). + # We use the highest resolution scale, 'scale0'. + # The image data is typically in the 'image' variable of the dataset. + img_arr = img["scale0"]["image"] + + all_channels = img_arr.coords["c"].values.tolist() + img_dtype = img_arr.dtype img_dtype_is_uint8 = img_dtype.kind == 'u' and img_dtype.itemsize == 1 # Not ideal logic. Should ideally only use the OME-NGFF color model metadata. But this is what spatialdata-plot does. @@ -631,7 +635,17 @@ def render_labels(self, # References: # - https://spatialdata.scverse.org/projects/plot/en/latest/plotting.html#spatialdata_plot.pl.basic.PlotAccessor.render_points # - https://github.com/scverse/spatialdata-plot/blob/c9bae235c0521499fb4d1098b15c79619654e5dc/src/spatialdata_plot/pl/basic.py#L338 - def render_points(self, element=None, **kwargs): + def render_points(self, + element=None, + color=None, + alpha=None, + groups=None, + palette=None, + # TODO: size + table_name=None, + table_layer=None, + **kwargs + ): """ Renders points. @@ -639,7 +653,17 @@ def render_points(self, element=None, **kwargs): :returns: Self, allows for chaining. """ if not VitesscePlotAccessor._is_enabled: - return self._pl.render_points(element=element, **kwargs) + return self._pl.render_points( + element=element, + color=color, + alpha=alpha, + groups=groups, + palette=palette, + # TODO: size + table_name=table_name, + table_layer=table_layer, + **kwargs + ) self._maybe_init() @@ -664,10 +688,83 @@ def render_points(self, element=None, **kwargs): "obsType": obs_type, "obsHighlight": None, "fileUid": file_uid, + # TODO: featureSelection: None or list[str] + # TODO: featureFilterMode: None or 'featureSelection' + # TODO: obsColorEncoding: 'geneSelection' or 'spatialLayerColor' or 'randomByFeature' or 'random' + # TODO: featureColor: None or [ { name: str, color: [R, G, B] } ] + # TODO: spatialLayerColor: [R, G, B] + # TODO: spatialLayerOpacity: number } + + # Coloring cases + # color param can be: + # - None (default color) + # - static color like "red" or "#FF0000" + # - name of the "feature_name" column of sdata.points table + # alpha param can be: + # - None (default opacity) 1.0 + # - float between 0.0 and 1.0 + + # groups param: + # - None (all points) + # - str: a single gene name (when `color` param is the name of the column containing gene names) + # - list[str]: list of gene names (when `color` param is the name of the column containing gene names) + + # palette param: + # - None (default color) + # - str: a single color (when `groups` param is a single gene name) + # - list[str]: list of colors (when `groups` param is a list of gene names) (the length must match the length of `groups`) + + ddf = self.sdata.points[element] + + obs_coordination = None + feature_coordination = None + + if color is not None: + if is_color_like(color): + # static color + layer_coordination["spatialLayerColor"] = to_uint8_rgb(color) + elif color in ddf.columns: + # feature_name column + layer_coordination["obsColorEncoding"] = "randomByFeature" + + if groups is not None: + if type(groups) is str: + groups = [groups] + if palette is not None: + if type(palette) is str: + # Broadcast single color to all groups. + palette = [palette for _ in groups] + elif type(palette) is list and len(groups) != len(palette): + raise ValueError("The length of 'groups' and 'palette' lists must be equal.") + + feature_color_val = [ + { + "name": groups[i], + "color": to_uint8_rgb(palette[i]), + } for i in range(len(groups)) + ] + + layer_coordination["featureColor"] = feature_color_val + layer_coordination["obsColorEncoding"] = "geneSelection" + layer_coordination["featureSelection"] = groups + layer_coordination["featureFilterMode"] = "featureSelection" + + feature_coordination = { + "obsType": obs_type, + "featureType": feature_type, + "featureSelection": groups if groups is not None else None, + } + + if alpha is not None: + layer_coordination["spatialLayerOpacity"] = alpha + + # TODO: perform the necessary operations on the points dataframe (sorting by morton code). + # Perhaps the user will need to opt-in via the global configuration. + self.point_layers.append( - (wrapper_args, layer_coordination) + (wrapper_args, layer_coordination, obs_coordination, feature_coordination) ) return self.sdata @@ -714,7 +811,7 @@ def show(self, coordinate_systems=None, **kwargs): }) dataset = dataset.add_object(spot_wrapper) - for (layer_wrapper_args, _) in self.point_layers: + for (layer_wrapper_args, _, _, _) in self.point_layers: points_wrapper = SpatialDataWrapper(**{ **self.shared_wrapper_args, **({ "coordinate_system": coordinate_systems } if coordinate_systems is not None else {}), @@ -746,6 +843,11 @@ def show(self, coordinate_systems=None, **kwargs): obs_coordination.append(obs_coord) if feature_coord is not None: feature_coordination.append(feature_coord) + for (_, _, obs_coord, feature_coord) in self.point_layers: + if obs_coord is not None: + obs_coordination.append(obs_coord) + if feature_coord is not None: + feature_coordination.append(feature_coord) # Add obsSet and featureList views. for obs_coord in obs_coordination: From 74959bdb790cbe8e4bb6a6845cc46a56503c45c3 Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Tue, 20 Jan 2026 17:35:35 -0500 Subject: [PATCH 2/7] Add xenium notebook --- docs/notebooks/spatialdata_xenium.ipynb | 300 ++++++++++++++++++++++++ src/easy_vitessce/spatialdata_plot.py | 28 ++- 2 files changed, 320 insertions(+), 8 deletions(-) create mode 100644 docs/notebooks/spatialdata_xenium.ipynb diff --git a/docs/notebooks/spatialdata_xenium.ipynb b/docs/notebooks/spatialdata_xenium.ipynb new file mode 100644 index 0000000..8b022b0 --- /dev/null +++ b/docs/notebooks/spatialdata_xenium.ipynb @@ -0,0 +1,300 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "013a95d1", + "metadata": {}, + "source": [ + "# EasyVitessce Example: SpatialData-Plot with MERFISH dataset" + ] + }, + { + "cell_type": "markdown", + "id": "40c9b340", + "metadata": {}, + "source": [ + "## Downloading and importing necessary packages" + ] + }, + { + "cell_type": "markdown", + "id": "846b644c", + "metadata": {}, + "source": [ + "By default, interactive plots are enabled upon importing easy_vitessce. This notebook aims to demonstrate the transition between static and interactive plots, so the interactive plots are initially turned off." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b9e634a", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install easy_vitessce\n", + "!pip install spatialdata\n", + "!pip install spatialdata_plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05991a70", + "metadata": {}, + "outputs": [], + "source": [ + "import easy_vitessce as ev \n", + "import spatialdata as sd\n", + "import spatialdata_plot\n", + "from os.path import join" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19bfbe14-2fa3-4d8a-b239-5e1385806bcb", + "metadata": {}, + "outputs": [], + "source": [ + "from vitessce.data_utils import (\n", + " sdata_morton_sort_points,\n", + " sdata_points_process_columns,\n", + " sdata_points_write_bounding_box_attrs,\n", + " sdata_points_modify_row_group_size,\n", + " sdata_morton_query_rect,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "594003c7", + "metadata": {}, + "outputs": [], + "source": [ + "# Disable interactive plots\n", + "ev.configure_plots(enable_plots=[\"spatialdata-plot\"])" + ] + }, + { + "cell_type": "markdown", + "id": "b0fec04c-68a2-4df6-8ec5-093812c98a6d", + "metadata": {}, + "source": [ + "## Download the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a7f7f9a-2529-41bb-98c5-bbde65867356", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from os.path import join, isfile, isdir\n", + "from urllib.request import urlretrieve\n", + "import zipfile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3016ea87-6388-4f43-8b92-6244d284b4e0", + "metadata": {}, + "outputs": [], + "source": [ + "data_dir = \"data\"\n", + "zip_path = join(data_dir, \"xenium_rep1_io.spatialdata.zarr.zip\")\n", + "sdata_path = join(data_dir, \"xenium_rep1_io.spatialdata.zarr\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b70f4ee6-f6da-49f0-8dba-9042259782b3", + "metadata": {}, + "outputs": [], + "source": [ + "if not isdir(sdata_path):\n", + " if not isfile(zip_path):\n", + " os.makedirs(data_dir, exist_ok=True)\n", + " urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/xenium_rep1_io.zip', zip_path)\n", + " with zipfile.ZipFile(zip_path,\"r\") as zip_ref:\n", + " zip_ref.extractall(data_dir)\n", + " os.rename(join(data_dir, \"data.zarr\"), sdata_path)" + ] + }, + { + "cell_type": "markdown", + "id": "f7bebbc5", + "metadata": {}, + "source": [ + "## Read the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e946174", + "metadata": {}, + "outputs": [], + "source": [ + "sdata = sd.read_zarr(sdata_path)\n", + "sdata" + ] + }, + { + "cell_type": "markdown", + "id": "d5fc0fd3-3c35-4851-bc31-375fc89c350c", + "metadata": {}, + "source": [ + "## Make the points ready for tiled access\n", + "\n", + "References:\n", + "- https://vitessce.io/docs/data-troubleshooting/#points\n", + "- https://github.com/vitessce/vitessce-python/blob/main/docs/notebooks/spatial_data_xenium_morton.ipynb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a4cb7c5-1ce2-4573-9c1c-176538ab25f3", + "metadata": {}, + "outputs": [], + "source": [ + "if \"transcripts_with_morton_codes\" not in sdata.points:\n", + " sdata = sdata_morton_sort_points(sdata, \"transcripts\")\n", + " \n", + " # Add feature_index column to dataframe, and reorder columns so that feature_name (dict column) is the rightmost column.\n", + " ddf = sdata_points_process_columns(sdata, \"transcripts\", var_name_col=\"feature_name\", table_name=\"table\")\n", + " \n", + " sdata[\"transcripts_with_morton_codes\"] = ddf\n", + " sdata.write_element(\"transcripts_with_morton_codes\")\n", + " \n", + " sdata_points_write_bounding_box_attrs(sdata, \"transcripts_with_morton_codes\")\n", + " \n", + " sdata_points_modify_row_group_size(sdata, \"transcripts_with_morton_codes\", row_group_size=25_000)" + ] + }, + { + "cell_type": "markdown", + "id": "140217fb", + "metadata": {}, + "source": [ + "## Static plotting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8383a98f-4090-44e2-b574-3531d3a96dfa", + "metadata": {}, + "outputs": [], + "source": [ + "sdata" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14f9d262-d853-456b-be5d-26a50975a047", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2035d286", + "metadata": {}, + "outputs": [], + "source": [ + "vw = (\n", + " sdata\n", + " .pl.render_images(element=\"morphology_focus\")\n", + " .pl.render_shapes(element=\"cell_boundaries\")\n", + " .pl.render_points(element=\"transcripts_with_morton_codes\", color=\"feature_name\", groups=[\"ERBB2\"], palette=[\"red\"])\n", + " .pl.show()\n", + ")\n", + "vw" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d69cdee", + "metadata": {}, + "outputs": [], + "source": [ + "# add another code block for the mouse liver dataset? " + ] + }, + { + "cell_type": "markdown", + "id": "27b04c12", + "metadata": {}, + "source": [ + "## Activating interactive plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ddf74968", + "metadata": {}, + "outputs": [], + "source": [ + "# Enable interactive plots\n", + "ev.configure_plots(enable_plots=[\"spatialdata-plot\"])" + ] + }, + { + "cell_type": "markdown", + "id": "23ffc21d-92ac-4066-b09a-b7a10307cbe6", + "metadata": {}, + "source": [ + "## Interactive plotting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63900fe4", + "metadata": {}, + "outputs": [], + "source": [ + "sdata.pl.render_images(element=\"rasterized\").pl.render_shapes(element=\"cells\", color=\"Acta2\").pl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ed91d03f-098b-47b7-a397-4b2003c60a13", + "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.12.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/easy_vitessce/spatialdata_plot.py b/src/easy_vitessce/spatialdata_plot.py index 3199343..59addac 100644 --- a/src/easy_vitessce/spatialdata_plot.py +++ b/src/easy_vitessce/spatialdata_plot.py @@ -29,15 +29,10 @@ from matplotlib.colors import is_color_like -# Internal function for shared logic between render_shapes and render_labels. -def _shared_render_shapes_and_labels( - sdata, element, table_name, table_layer, color, cmap, norm, groups, palette, obs_type, feature_type, is_spots, fill_alpha, outline_alpha, outline_width, outline_color, +def _shared_table_handling(sdata, element, table_name, table_layer, obs_type, feature_type, # Note: These dict params are modified by this function. wrapper_args, obs_type_to_num_rows, feature_type_to_num_rows, ): - - extra_layer_coordination = {} - if table_name is None: annotating_tables = list(get_element_annotators(sdata, element)) if len(annotating_tables) > 0: @@ -80,6 +75,19 @@ def _shared_render_shapes_and_labels( """ pass +# Internal function for shared logic between render_shapes and render_labels. +def _shared_render_shapes_and_labels( + sdata, element, table_name, table_layer, color, cmap, norm, groups, palette, obs_type, feature_type, is_spots, fill_alpha, outline_alpha, outline_width, outline_color, + # Note: These dict params are modified by this function. + wrapper_args, obs_type_to_num_rows, feature_type_to_num_rows, + ): + + extra_layer_coordination = {} + + _shared_table_handling(sdata, element, table_name, table_layer, obs_type, feature_type, + wrapper_args, obs_type_to_num_rows, feature_type_to_num_rows, + ) + obs_coordination = None feature_coordination = None is_maybe_static_color = False @@ -240,7 +248,7 @@ def _init_params(self): # Tuples of (wrapper_args, spot_layer_coordination, obs_coordination, feature_coordination) ] self.point_layers = [ - # Tuples of (wrapper_args, point_layer_coordination) + # Tuples of (wrapper_args, point_layer_coordination, obs_coordination, feature_coordination) ] # For ensuring that counts of obs/var match if used for multiple layers. @@ -684,6 +692,10 @@ def render_points(self, } } + _shared_table_handling(self.sdata, element, table_name, table_layer, obs_type, feature_type, + wrapper_args, self.obs_type_to_num_rows, self.feature_type_to_num_rows, + ) + layer_coordination = { "obsType": obs_type, "obsHighlight": None, @@ -953,7 +965,7 @@ def show(self, coordinate_systems=None, **kwargs): **obs_coordination_by_key.get(layer_dict.get("obsType"), {}), **layer_dict, } - for (_, layer_dict) in self.point_layers + for (_, layer_dict, _, _) in self.point_layers ]), }, meta=True, scope_prefix=get_initial_coordination_scope_prefix(dataset_uid, "obsPoints")) From bea5ac434492d331455dc1f955ce6c1aa2d3fd24 Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:05:10 -0500 Subject: [PATCH 3/7] Update blobs notebook --- docs/notebooks/spatialdata_blobs.ipynb | 54 +++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/notebooks/spatialdata_blobs.ipynb b/docs/notebooks/spatialdata_blobs.ipynb index e25eedb..c257aed 100644 --- a/docs/notebooks/spatialdata_blobs.ipynb +++ b/docs/notebooks/spatialdata_blobs.ipynb @@ -29,7 +29,10 @@ "source": [ "import easy_vitessce as ev\n", "import spatialdata\n", - "import spatialdata_plot" + "import spatialdata_plot\n", + "from anndata import AnnData\n", + "import pandas as pd\n", + "from vitessce.data_utils import sdata_points_process_columns" ] }, { @@ -74,6 +77,55 @@ "sdata" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create a feature_index column for the Points table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ddf = sdata.points['blobs_points']\n", + "ddf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(sdata.tables['table'].var.index.tolist())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The sdata.tables['table'].var table does not contain genes as indices; it contains channels.\n", + "# We need to create another table which has a var.index column containing the gene IDs for the points.\n", + "unique_gene_ids = ddf[\"genes\"].unique().compute().tolist()\n", + "points_var_df = pd.DataFrame(index=unique_gene_ids, data=[], columns=[])\n", + "points_table = AnnData(var=points_var_df, obs=None, X=None)\n", + "sdata.tables['table_points'] = points_table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sdata.points['blobs_points'] = sdata_points_process_columns(sdata, \"blobs_points\", var_name_col=\"genes\", table_name=\"table_points\")" + ] + }, { "cell_type": "markdown", "metadata": {}, From 211551b070128ade08d52b5e1211e80fbce0f793 Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:45:16 -0500 Subject: [PATCH 4/7] WIP: points support. --- docs/notebooks/spatialdata_blobs.ipynb | 239 ++++++++++++++++-- docs/notebooks/spatialdata_xenium.ipynb | 308 ++++++++++++++++++++++-- src/easy_vitessce/spatialdata_plot.py | 18 ++ uv.lock | 8 +- 4 files changed, 531 insertions(+), 42 deletions(-) diff --git a/docs/notebooks/spatialdata_blobs.ipynb b/docs/notebooks/spatialdata_blobs.ipynb index c257aed..623d18b 100644 --- a/docs/notebooks/spatialdata_blobs.ipynb +++ b/docs/notebooks/spatialdata_blobs.ipynb @@ -21,11 +21,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/dask/dataframe/__init__.py:31: FutureWarning: The legacy Dask DataFrame implementation is deprecated and will be removed in a future version. Set the configuration option `dataframe.query-planning` to `True` or None to enable the new Dask Dataframe implementation and silence this warning.\n", + " warnings.warn(\n", + "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/xarray_schema/__init__.py:1: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n", + " from pkg_resources import DistributionNotFound, get_distribution\n" + ] + } + ], "source": [ "import easy_vitessce as ev\n", "import spatialdata\n", @@ -37,9 +48,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ev.config.set({ 'data.overwrite': True })" ] @@ -53,7 +75,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -69,9 +91,45 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/spatialdata/models/models.py:1144: UserWarning: Converting `region_key: region` to categorical dtype.\n", + " return convert_region_column_to_categorical(adata)\n" + ] + }, + { + "data": { + "text/plain": [ + "SpatialData object\n", + "├── Images\n", + "│ ├── 'blobs_image': DataArray[cyx] (3, 512, 512)\n", + "│ └── 'blobs_multiscale_image': DataTree[cyx] (3, 512, 512), (3, 256, 256), (3, 128, 128)\n", + "├── Labels\n", + "│ ├── 'blobs_labels': DataArray[yx] (512, 512)\n", + "│ └── 'blobs_multiscale_labels': DataTree[yx] (512, 512), (256, 256), (128, 128)\n", + "├── Points\n", + "│ └── 'blobs_points': DataFrame with shape: (, 4) (2D points)\n", + "├── Shapes\n", + "│ ├── 'blobs_circles': GeoDataFrame shape: (5, 2) (2D shapes)\n", + "│ ├── 'blobs_multipolygons': GeoDataFrame shape: (2, 1) (2D shapes)\n", + "│ └── 'blobs_polygons': GeoDataFrame shape: (5, 1) (2D shapes)\n", + "└── Tables\n", + " └── 'table': AnnData (26, 3)\n", + "with coordinate systems:\n", + " ▸ 'global', with elements:\n", + " blobs_image (Images), blobs_multiscale_image (Images), blobs_labels (Labels), blobs_multiscale_labels (Labels), blobs_points (Points), blobs_circles (Shapes), blobs_multipolygons (Shapes), blobs_polygons (Shapes)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "sdata = spatialdata.datasets.blobs()\n", "sdata" @@ -86,9 +144,78 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
Dask DataFrame Structure:
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xygenesinstance_id
npartitions=1
0int64int64category[known]int64
199............
\n", + "
\n", + "
Dask Name: from_pandas, 1 graph layer
" + ], + "text/plain": [ + "Dask DataFrame Structure:\n", + " x y genes instance_id\n", + "npartitions=1 \n", + "0 int64 int64 category[known] int64\n", + "199 ... ... ... ...\n", + "Dask Name: from_pandas, 1 graph layer" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ddf = sdata.points['blobs_points']\n", "ddf" @@ -96,16 +223,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['channel_0_sum', 'channel_1_sum', 'channel_2_sum']\n" + ] + } + ], "source": [ "print(sdata.tables['table'].var.index.tolist())" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -119,13 +254,47 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/dask/dataframe/core.py:4448: UserWarning: \n", + "You did not provide metadata, so Dask is running your function on a small dataset to guess output types. It is possible that Dask will guess incorrectly.\n", + "To provide an explicit output types or to silence this message, please provide the `meta=` keyword, as described in the map or apply function that you are using.\n", + " Before: .apply(func)\n", + " After: .apply(func, meta=('genes', 'category'))\n", + "\n", + " warnings.warn(meta_warning(meta))\n", + "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/spatialdata/_core/_elements.py:115: UserWarning: Key `blobs_points` already exists. Overwriting it in-memory.\n", + " self._check_key(key, self.keys(), self._shared_keys)\n" + ] + } + ], "source": [ "sdata.points['blobs_points'] = sdata_points_process_columns(sdata, \"blobs_points\", var_name_col=\"genes\", table_name=\"table_points\")" ] }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[34mINFO \u001b[0m The Zarr backing store has been changed from data/\u001b[93m1953eb5a-d483-4694-ad80-4c247689706a\u001b[0m.sdata.zarr the new \n", + " file path: data/blobs_with_feature_index.sdata.zarr \n" + ] + } + ], + "source": [ + "sdata.write('data/blobs_with_feature_index.sdata.zarr')" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -135,9 +304,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5ff620a24d5d47d5b87af121e7f1a7a1", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "VitessceWidget(js_dev_mode=True, uid='5508')" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "sdata.pl.render_images(\"blobs_image\").pl.render_labels(\"blobs_labels\").pl.show()" ] @@ -151,11 +336,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f088ce8aa15c40ba9628e1cadc59fc02", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "VitessceWidget(js_dev_mode=True, uid='b28f')" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "sdata.pl.render_images(\"blobs_image\").pl.render_labels(\"blobs_labels\", color=\"channel_0_sum\").pl.render_points(\"blobs_points\").pl.show()" + "sdata.pl.render_images(\"blobs_image\").pl.render_labels(\"blobs_labels\", color=\"channel_0_sum\", table_name=\"table\").pl.render_points(\"blobs_points\").pl.show()" ] }, { diff --git a/docs/notebooks/spatialdata_xenium.ipynb b/docs/notebooks/spatialdata_xenium.ipynb index 8b022b0..e9fb85a 100644 --- a/docs/notebooks/spatialdata_xenium.ipynb +++ b/docs/notebooks/spatialdata_xenium.ipynb @@ -38,10 +38,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "05991a70", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/dask/dataframe/__init__.py:31: FutureWarning: The legacy Dask DataFrame implementation is deprecated and will be removed in a future version. Set the configuration option `dataframe.query-planning` to `True` or None to enable the new Dask Dataframe implementation and silence this warning.\n", + " warnings.warn(\n", + "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/xarray_schema/__init__.py:1: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n", + " from pkg_resources import DistributionNotFound, get_distribution\n" + ] + } + ], "source": [ "import easy_vitessce as ev \n", "import spatialdata as sd\n", @@ -51,7 +62,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "19bfbe14-2fa3-4d8a-b239-5e1385806bcb", "metadata": {}, "outputs": [], @@ -67,10 +78,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "594003c7", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Warning: SpatialData.pl has already been monkeypatched.\n" + ] + } + ], "source": [ "# Disable interactive plots\n", "ev.configure_plots(enable_plots=[\"spatialdata-plot\"])" @@ -86,7 +105,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "7a7f7f9a-2529-41bb-98c5-bbde65867356", "metadata": {}, "outputs": [], @@ -99,7 +118,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "3016ea87-6388-4f43-8b92-6244d284b4e0", "metadata": {}, "outputs": [], @@ -111,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "b70f4ee6-f6da-49f0-8dba-9042259782b3", "metadata": {}, "outputs": [], @@ -135,10 +154,45 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "4e946174", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "version mismatch: detected: RasterFormatV02, requested: FormatV04\n", + "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/zarr/creation.py:610: UserWarning: ignoring keyword argument 'read_only'\n", + " compressor, fill_value = _kwargs_compat(compressor, fill_value, kwargs)\n", + "version mismatch: detected: RasterFormatV02, requested: FormatV04\n" + ] + }, + { + "data": { + "text/plain": [ + "SpatialData object, with associated Zarr store: /Users/mkeller/research/dbmi/vitessce/easy_vitessce/docs/notebooks/data/xenium_rep1_io.spatialdata.zarr\n", + "├── Images\n", + "│ ├── 'morphology_focus': DataTree[cyx] (1, 25778, 35416), (1, 12889, 17708), (1, 6444, 8854), (1, 3222, 4427), (1, 1611, 2213)\n", + "│ └── 'morphology_mip': DataTree[cyx] (1, 25778, 35416), (1, 12889, 17708), (1, 6444, 8854), (1, 3222, 4427), (1, 1611, 2213)\n", + "├── Points\n", + "│ ├── 'transcripts': DataFrame with shape: (, 8) (3D points)\n", + "│ └── 'transcripts_with_morton_codes': DataFrame with shape: (, 12) (3D points)\n", + "├── Shapes\n", + "│ ├── 'cell_boundaries': GeoDataFrame shape: (167780, 1) (2D shapes)\n", + "│ └── 'cell_circles': GeoDataFrame shape: (167780, 2) (2D shapes)\n", + "└── Tables\n", + " └── 'table': AnnData (167780, 313)\n", + "with coordinate systems:\n", + " ▸ 'global', with elements:\n", + " morphology_focus (Images), morphology_mip (Images), transcripts (Points), transcripts_with_morton_codes (Points), cell_boundaries (Shapes), cell_circles (Shapes)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "sdata = sd.read_zarr(sdata_path)\n", "sdata" @@ -158,7 +212,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "4a4cb7c5-1ce2-4573-9c1c-176538ab25f3", "metadata": {}, "outputs": [], @@ -187,28 +241,244 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "8383a98f-4090-44e2-b574-3531d3a96dfa", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "SpatialData object, with associated Zarr store: /Users/mkeller/research/dbmi/vitessce/easy_vitessce/docs/notebooks/data/xenium_rep1_io.spatialdata.zarr\n", + "├── Images\n", + "│ ├── 'morphology_focus': DataTree[cyx] (1, 25778, 35416), (1, 12889, 17708), (1, 6444, 8854), (1, 3222, 4427), (1, 1611, 2213)\n", + "│ └── 'morphology_mip': DataTree[cyx] (1, 25778, 35416), (1, 12889, 17708), (1, 6444, 8854), (1, 3222, 4427), (1, 1611, 2213)\n", + "├── Points\n", + "│ ├── 'transcripts': DataFrame with shape: (, 8) (3D points)\n", + "│ └── 'transcripts_with_morton_codes': DataFrame with shape: (, 12) (3D points)\n", + "├── Shapes\n", + "│ ├── 'cell_boundaries': GeoDataFrame shape: (167780, 1) (2D shapes)\n", + "│ └── 'cell_circles': GeoDataFrame shape: (167780, 2) (2D shapes)\n", + "└── Tables\n", + " └── 'table': AnnData (167780, 313)\n", + "with coordinate systems:\n", + " ▸ 'global', with elements:\n", + " morphology_focus (Images), morphology_mip (Images), transcripts (Points), transcripts_with_morton_codes (Points), cell_boundaries (Shapes), cell_circles (Shapes)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "sdata" ] }, { "cell_type": "code", - "execution_count": null, - "id": "14f9d262-d853-456b-be5d-26a50975a047", + "execution_count": 11, + "id": "e5a21a04-8b45-4f69-9115-98341bd7a76a", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/html": [ + "
Dask DataFrame Structure:
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xyzcell_idoverlaps_nucleustranscript_idqvx_uinty_uintmorton_code_2dfeature_name_codesfeature_name
npartitions=8
float32float32float32int32uint8uint64float32uint32uint32uint32int32category[unknown]
....................................
.......................................
....................................
....................................
\n", + "
\n", + "
Dask Name: read-parquet, 1 graph layer
" + ], + "text/plain": [ + "Dask DataFrame Structure:\n", + " x y z cell_id overlaps_nucleus transcript_id qv x_uint y_uint morton_code_2d feature_name_codes feature_name\n", + "npartitions=8 \n", + " float32 float32 float32 int32 uint8 uint64 float32 uint32 uint32 uint32 int32 category[unknown]\n", + " ... ... ... ... ... ... ... ... ... ... ... ...\n", + "... ... ... ... ... ... ... ... ... ... ... ... ...\n", + " ... ... ... ... ... ... ... ... ... ... ... ...\n", + " ... ... ... ... ... ... ... ... ... ... ... ...\n", + "Dask Name: read-parquet, 1 graph layer" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sdata.points[\"transcripts_with_morton_codes\"]" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, + "id": "1b41a4bb-e552-41c7-a0c4-f9b70847d5cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'transform': {'global': Scale (x, y, z)\n", + " [4.70588235 4.70588235 1. ]},\n", + " 'spatialdata_attrs': {'feature_key': 'feature_name',\n", + " 'instance_key': 'cell_id'}}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sdata.points[\"transcripts_with_morton_codes\"].attrs" + ] + }, + { + "cell_type": "code", + "execution_count": 10, "id": "2035d286", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "aca392bdcba64975bf8f8c088c5e6e49", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "VitessceWidget(js_dev_mode=True, uid='eed7')" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "vw = (\n", " sdata\n", diff --git a/src/easy_vitessce/spatialdata_plot.py b/src/easy_vitessce/spatialdata_plot.py index 59addac..eb8e0d3 100644 --- a/src/easy_vitessce/spatialdata_plot.py +++ b/src/easy_vitessce/spatialdata_plot.py @@ -729,6 +729,18 @@ def render_points(self, # - list[str]: list of colors (when `groups` param is a list of gene names) (the length must match the length of `groups`) ddf = self.sdata.points[element] + + try: + # Check if the dataframe contains a _codes column. + # Reference: https://github.com/vitessce/vitessce-python/blob/adb066c088307b658a45ca9cf2ab2d63effaa5ef/src/vitessce/data_utils/spatialdata_points_zorder.py#L458 + feature_key_col = ddf.attrs["spatialdata_attrs"]["feature_key"] + # Note: this should be the default behavior on the JS side, so this may be unnecessary to do here. + # Reference: https://github.com/vitessce/vitessce/blob/0ff9f3b43ca28ef9858d3db0ce06417f6dd174a9/packages/file-types/spatial-zarr/src/spatialdata-loaders/SpatialDataObsPointsLoader.js#L126 + codes_col = f"{feature_key_col}_codes" + if codes_col in ddf.columns: + wrapper_args["obs_points_feature_index_column"] = codes_col + except KeyError: + pass obs_coordination = None feature_coordination = None @@ -741,6 +753,12 @@ def render_points(self, # feature_name column layer_coordination["obsColorEncoding"] = "randomByFeature" + # In case the value of `color` does not match spatialdata_attrs.feature_key + codes_col = f"{color}_codes" + if codes_col in ddf.columns: + # Note: this overwrites any existing feature index column value in the dict. + wrapper_args["obs_points_feature_index_column"] = codes_col + if groups is not None: if type(groups) is str: groups = [groups] diff --git a/uv.lock b/uv.lock index 6fdb3c4..2f1c6b3 100644 --- a/uv.lock +++ b/uv.lock @@ -1023,7 +1023,7 @@ wheels = [ [[package]] name = "easy-vitessce" -version = "0.0.8" +version = "0.0.10" source = { editable = "." } dependencies = [ { name = "anndata", version = "0.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -1085,7 +1085,7 @@ requires-dist = [ { name = "sphinx", marker = "extra == 'docs'" }, { name = "sphinx-book-theme", marker = "extra == 'docs'" }, { name = "sphinx-copybutton", marker = "extra == 'docs'" }, - { name = "vitessce", extras = ["all"], specifier = ">=3.7.9" }, + { name = "vitessce", extras = ["all"], specifier = ">=3.8.0" }, { name = "xarray", specifier = ">=2024.10.0,<=2025.3.0" }, { name = "zarr", specifier = ">=2.5.0,<3" }, ] @@ -4904,7 +4904,7 @@ wheels = [ [[package]] name = "vitessce" -version = "3.7.9" +version = "3.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "black" }, @@ -4918,7 +4918,7 @@ dependencies = [ { name = "zarr", version = "2.18.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/79/ef9dcc1dc0ead62e143194606b65a00761f43dbc22df3bca248c1c78115d/vitessce-3.7.9-py3-none-any.whl", hash = "sha256:b54cc4a73991d07b1ccf2617062705ae708508a18fde338790b7fea381ee30ba", size = 79840 }, + { url = "https://files.pythonhosted.org/packages/d3/60/b5c82868f136eff8c2d3a8cae675c63b4e9e65b10f6c36861d49aade32ce/vitessce-3.8.0-py3-none-any.whl", hash = "sha256:763f9b8f411d103cd577789acba2baa16b938969826d6ead10dde46f916dda4a", size = 79843 }, ] [package.optional-dependencies] From 9d1b09215efe357459d8ada5dad0de8104f92410 Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Thu, 5 Feb 2026 13:09:44 -0500 Subject: [PATCH 5/7] Update blobs notebook --- docs/notebooks/spatialdata_blobs.ipynb | 281 ++++++------------------- pyproject.toml | 2 +- 2 files changed, 60 insertions(+), 223 deletions(-) diff --git a/docs/notebooks/spatialdata_blobs.ipynb b/docs/notebooks/spatialdata_blobs.ipynb index 623d18b..88d3f4c 100644 --- a/docs/notebooks/spatialdata_blobs.ipynb +++ b/docs/notebooks/spatialdata_blobs.ipynb @@ -21,47 +21,24 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/dask/dataframe/__init__.py:31: FutureWarning: The legacy Dask DataFrame implementation is deprecated and will be removed in a future version. Set the configuration option `dataframe.query-planning` to `True` or None to enable the new Dask Dataframe implementation and silence this warning.\n", - " warnings.warn(\n", - "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/xarray_schema/__init__.py:1: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n", - " from pkg_resources import DistributionNotFound, get_distribution\n" - ] - } - ], + "outputs": [], "source": [ "import easy_vitessce as ev\n", "import spatialdata\n", "import spatialdata_plot\n", "from anndata import AnnData\n", - "import pandas as pd\n", - "from vitessce.data_utils import sdata_points_process_columns" + "import pandas as pd" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ev.config.set({ 'data.overwrite': True })" ] @@ -75,11 +52,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# ev.config.set({ 'data.wrapper_param_suffix': '_store' })" + "# ev.config.set({ 'data.wrapper_param_suffix': '_store' }) # Caveat: https://github.com/vitessce/vitessce-python/issues/491" ] }, { @@ -91,45 +68,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/spatialdata/models/models.py:1144: UserWarning: Converting `region_key: region` to categorical dtype.\n", - " return convert_region_column_to_categorical(adata)\n" - ] - }, - { - "data": { - "text/plain": [ - "SpatialData object\n", - "├── Images\n", - "│ ├── 'blobs_image': DataArray[cyx] (3, 512, 512)\n", - "│ └── 'blobs_multiscale_image': DataTree[cyx] (3, 512, 512), (3, 256, 256), (3, 128, 128)\n", - "├── Labels\n", - "│ ├── 'blobs_labels': DataArray[yx] (512, 512)\n", - "│ └── 'blobs_multiscale_labels': DataTree[yx] (512, 512), (256, 256), (128, 128)\n", - "├── Points\n", - "│ └── 'blobs_points': DataFrame with shape: (, 4) (2D points)\n", - "├── Shapes\n", - "│ ├── 'blobs_circles': GeoDataFrame shape: (5, 2) (2D shapes)\n", - "│ ├── 'blobs_multipolygons': GeoDataFrame shape: (2, 1) (2D shapes)\n", - "│ └── 'blobs_polygons': GeoDataFrame shape: (5, 1) (2D shapes)\n", - "└── Tables\n", - " └── 'table': AnnData (26, 3)\n", - "with coordinate systems:\n", - " ▸ 'global', with elements:\n", - " blobs_image (Images), blobs_multiscale_image (Images), blobs_labels (Labels), blobs_multiscale_labels (Labels), blobs_points (Points), blobs_circles (Shapes), blobs_multipolygons (Shapes), blobs_polygons (Shapes)" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "sdata = spatialdata.datasets.blobs()\n", "sdata" @@ -139,83 +80,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Create a feature_index column for the Points table" + "### Create a Table with a `var` dataframe corresponding to the `genes` column of the Points table" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
Dask DataFrame Structure:
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xygenesinstance_id
npartitions=1
0int64int64category[known]int64
199............
\n", - "
\n", - "
Dask Name: from_pandas, 1 graph layer
" - ], - "text/plain": [ - "Dask DataFrame Structure:\n", - " x y genes instance_id\n", - "npartitions=1 \n", - "0 int64 int64 category[known] int64\n", - "199 ... ... ... ...\n", - "Dask Name: from_pandas, 1 graph layer" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ddf = sdata.points['blobs_points']\n", "ddf" @@ -223,28 +95,21 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['channel_0_sum', 'channel_1_sum', 'channel_2_sum']\n" - ] - } - ], + "outputs": [], "source": [ + "# The current `table` element contains a `var` dataframe corresponding\n", + "# to the 3 image channels, rather than the 2 gene IDs.\n", "print(sdata.tables['table'].var.index.tolist())" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# The sdata.tables['table'].var table does not contain genes as indices; it contains channels.\n", "# We need to create another table which has a var.index column containing the gene IDs for the points.\n", "unique_gene_ids = ddf[\"genes\"].unique().compute().tolist()\n", "points_var_df = pd.DataFrame(index=unique_gene_ids, data=[], columns=[])\n", @@ -254,45 +119,20 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/dask/dataframe/core.py:4448: UserWarning: \n", - "You did not provide metadata, so Dask is running your function on a small dataset to guess output types. It is possible that Dask will guess incorrectly.\n", - "To provide an explicit output types or to silence this message, please provide the `meta=` keyword, as described in the map or apply function that you are using.\n", - " Before: .apply(func)\n", - " After: .apply(func, meta=('genes', 'category'))\n", - "\n", - " warnings.warn(meta_warning(meta))\n", - "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/spatialdata/_core/_elements.py:115: UserWarning: Key `blobs_points` already exists. Overwriting it in-memory.\n", - " self._check_key(key, self.keys(), self._shared_keys)\n" - ] - } - ], + "outputs": [], "source": [ - "sdata.points['blobs_points'] = sdata_points_process_columns(sdata, \"blobs_points\", var_name_col=\"genes\", table_name=\"table_points\")" + "unique_gene_ids" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[34mINFO \u001b[0m The Zarr backing store has been changed from data/\u001b[93m1953eb5a-d483-4694-ad80-4c247689706a\u001b[0m.sdata.zarr the new \n", - " file path: data/blobs_with_feature_index.sdata.zarr \n" - ] - } - ], + "outputs": [], "source": [ - "sdata.write('data/blobs_with_feature_index.sdata.zarr')" + "sdata.write('data/blobs.sdata.zarr', overwrite=True)" ] }, { @@ -304,25 +144,9 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5ff620a24d5d47d5b87af121e7f1a7a1", - "version_major": 2, - "version_minor": 1 - }, - "text/plain": [ - "VitessceWidget(js_dev_mode=True, uid='5508')" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "sdata.pl.render_images(\"blobs_image\").pl.render_labels(\"blobs_labels\").pl.show()" ] @@ -336,25 +160,9 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f088ce8aa15c40ba9628e1cadc59fc02", - "version_major": 2, - "version_minor": 1 - }, - "text/plain": [ - "VitessceWidget(js_dev_mode=True, uid='b28f')" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "sdata.pl.render_images(\"blobs_image\").pl.render_labels(\"blobs_labels\", color=\"channel_0_sum\", table_name=\"table\").pl.render_points(\"blobs_points\").pl.show()" ] @@ -363,7 +171,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Disable EasyVitessce" + "## Rendering a subset of points" ] }, { @@ -372,7 +180,13 @@ "metadata": {}, "outputs": [], "source": [ - "ev.disable_plots([\"spatialdata-plot\"])" + "vw = (sdata\n", + " .pl.render_images(\"blobs_image\")\n", + " #.pl.render_labels(\"blobs_labels\", color=\"channel_0_sum\", table_name=\"table\")\n", + " .pl.render_points(\"blobs_points\", color=\"genes\", groups=[\"gene_a\", \"gene_b\"], palette=[\"orange\", \"yellow\"], table_name=\"table_points\", alpha=1.0)\n", + " .pl.show()\n", + ")\n", + "vw" ] }, { @@ -388,6 +202,7 @@ "metadata": {}, "outputs": [], "source": [ + "ev.disable_plots([\"spatialdata-plot\"])\n", "sdata.pl.render_images(\"blobs_image\").pl.render_labels(\"blobs_labels\").pl.show()" ] }, @@ -397,9 +212,31 @@ "metadata": {}, "outputs": [], "source": [ - "sdata.pl.render_images(\"blobs_image\").pl.render_labels(\"blobs_labels\", color=\"channel_0_sum\").pl.render_points(\"blobs_points\").pl.show()" + "sdata.pl.render_images(\"blobs_image\").pl.render_labels(\"blobs_labels\", color=\"channel_0_sum\", table_name=\"table\").pl.render_points(\"blobs_points\").pl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vw = (sdata\n", + " .pl.render_images(\"blobs_image\")\n", + " #.pl.render_labels(\"blobs_labels\", color=\"channel_0_sum\", table_name=\"table\")\n", + " .pl.render_points(\"blobs_points\", color=\"genes\", groups=[\"gene_a\", \"gene_b\"], palette=[\"orange\", \"yellow\"], table_name=\"table_points\", alpha=1.0)\n", + " .pl.show()\n", + ")\n", + "vw" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, diff --git a/pyproject.toml b/pyproject.toml index c8b0b9f..c08f328 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ 'numpy>=1.21.2', 'zarr>=2.5.0,<3', 'numcodecs>=0.5.7,<0.16.0', - 'vitessce[all]>=3.8.0', + 'vitessce[all]>=3.8.1', 'scanpy>=1.11.3', 'anndata>=0.11.4', 'spatialdata>=0.3.0', From 0a93ae706bfbeeca3e5ad1dc075902b8ca883113 Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Thu, 5 Feb 2026 15:07:43 -0500 Subject: [PATCH 6/7] Update xenium nb --- docs/notebooks/spatialdata_xenium.ipynb | 308 +++--------------------- uv.lock | 7 +- 2 files changed, 34 insertions(+), 281 deletions(-) diff --git a/docs/notebooks/spatialdata_xenium.ipynb b/docs/notebooks/spatialdata_xenium.ipynb index e9fb85a..68e8d01 100644 --- a/docs/notebooks/spatialdata_xenium.ipynb +++ b/docs/notebooks/spatialdata_xenium.ipynb @@ -38,21 +38,10 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "05991a70", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/dask/dataframe/__init__.py:31: FutureWarning: The legacy Dask DataFrame implementation is deprecated and will be removed in a future version. Set the configuration option `dataframe.query-planning` to `True` or None to enable the new Dask Dataframe implementation and silence this warning.\n", - " warnings.warn(\n", - "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/xarray_schema/__init__.py:1: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n", - " from pkg_resources import DistributionNotFound, get_distribution\n" - ] - } - ], + "outputs": [], "source": [ "import easy_vitessce as ev \n", "import spatialdata as sd\n", @@ -62,7 +51,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "19bfbe14-2fa3-4d8a-b239-5e1385806bcb", "metadata": {}, "outputs": [], @@ -78,18 +67,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "594003c7", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Warning: SpatialData.pl has already been monkeypatched.\n" - ] - } - ], + "outputs": [], "source": [ "# Disable interactive plots\n", "ev.configure_plots(enable_plots=[\"spatialdata-plot\"])" @@ -105,7 +86,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "7a7f7f9a-2529-41bb-98c5-bbde65867356", "metadata": {}, "outputs": [], @@ -118,7 +99,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "3016ea87-6388-4f43-8b92-6244d284b4e0", "metadata": {}, "outputs": [], @@ -130,7 +111,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "b70f4ee6-f6da-49f0-8dba-9042259782b3", "metadata": {}, "outputs": [], @@ -154,45 +135,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "4e946174", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "version mismatch: detected: RasterFormatV02, requested: FormatV04\n", - "/Users/mkeller/research/dbmi/vitessce/easy_vitessce/.venv/lib/python3.12/site-packages/zarr/creation.py:610: UserWarning: ignoring keyword argument 'read_only'\n", - " compressor, fill_value = _kwargs_compat(compressor, fill_value, kwargs)\n", - "version mismatch: detected: RasterFormatV02, requested: FormatV04\n" - ] - }, - { - "data": { - "text/plain": [ - "SpatialData object, with associated Zarr store: /Users/mkeller/research/dbmi/vitessce/easy_vitessce/docs/notebooks/data/xenium_rep1_io.spatialdata.zarr\n", - "├── Images\n", - "│ ├── 'morphology_focus': DataTree[cyx] (1, 25778, 35416), (1, 12889, 17708), (1, 6444, 8854), (1, 3222, 4427), (1, 1611, 2213)\n", - "│ └── 'morphology_mip': DataTree[cyx] (1, 25778, 35416), (1, 12889, 17708), (1, 6444, 8854), (1, 3222, 4427), (1, 1611, 2213)\n", - "├── Points\n", - "│ ├── 'transcripts': DataFrame with shape: (, 8) (3D points)\n", - "│ └── 'transcripts_with_morton_codes': DataFrame with shape: (, 12) (3D points)\n", - "├── Shapes\n", - "│ ├── 'cell_boundaries': GeoDataFrame shape: (167780, 1) (2D shapes)\n", - "│ └── 'cell_circles': GeoDataFrame shape: (167780, 2) (2D shapes)\n", - "└── Tables\n", - " └── 'table': AnnData (167780, 313)\n", - "with coordinate systems:\n", - " ▸ 'global', with elements:\n", - " morphology_focus (Images), morphology_mip (Images), transcripts (Points), transcripts_with_morton_codes (Points), cell_boundaries (Shapes), cell_circles (Shapes)" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "sdata = sd.read_zarr(sdata_path)\n", "sdata" @@ -212,7 +158,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "4a4cb7c5-1ce2-4573-9c1c-176538ab25f3", "metadata": {}, "outputs": [], @@ -241,255 +187,61 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "8383a98f-4090-44e2-b574-3531d3a96dfa", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SpatialData object, with associated Zarr store: /Users/mkeller/research/dbmi/vitessce/easy_vitessce/docs/notebooks/data/xenium_rep1_io.spatialdata.zarr\n", - "├── Images\n", - "│ ├── 'morphology_focus': DataTree[cyx] (1, 25778, 35416), (1, 12889, 17708), (1, 6444, 8854), (1, 3222, 4427), (1, 1611, 2213)\n", - "│ └── 'morphology_mip': DataTree[cyx] (1, 25778, 35416), (1, 12889, 17708), (1, 6444, 8854), (1, 3222, 4427), (1, 1611, 2213)\n", - "├── Points\n", - "│ ├── 'transcripts': DataFrame with shape: (, 8) (3D points)\n", - "│ └── 'transcripts_with_morton_codes': DataFrame with shape: (, 12) (3D points)\n", - "├── Shapes\n", - "│ ├── 'cell_boundaries': GeoDataFrame shape: (167780, 1) (2D shapes)\n", - "│ └── 'cell_circles': GeoDataFrame shape: (167780, 2) (2D shapes)\n", - "└── Tables\n", - " └── 'table': AnnData (167780, 313)\n", - "with coordinate systems:\n", - " ▸ 'global', with elements:\n", - " morphology_focus (Images), morphology_mip (Images), transcripts (Points), transcripts_with_morton_codes (Points), cell_boundaries (Shapes), cell_circles (Shapes)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "sdata" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "e5a21a04-8b45-4f69-9115-98341bd7a76a", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
Dask DataFrame Structure:
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xyzcell_idoverlaps_nucleustranscript_idqvx_uinty_uintmorton_code_2dfeature_name_codesfeature_name
npartitions=8
float32float32float32int32uint8uint64float32uint32uint32uint32int32category[unknown]
....................................
.......................................
....................................
....................................
\n", - "
\n", - "
Dask Name: read-parquet, 1 graph layer
" - ], - "text/plain": [ - "Dask DataFrame Structure:\n", - " x y z cell_id overlaps_nucleus transcript_id qv x_uint y_uint morton_code_2d feature_name_codes feature_name\n", - "npartitions=8 \n", - " float32 float32 float32 int32 uint8 uint64 float32 uint32 uint32 uint32 int32 category[unknown]\n", - " ... ... ... ... ... ... ... ... ... ... ... ...\n", - "... ... ... ... ... ... ... ... ... ... ... ... ...\n", - " ... ... ... ... ... ... ... ... ... ... ... ...\n", - " ... ... ... ... ... ... ... ... ... ... ... ...\n", - "Dask Name: read-parquet, 1 graph layer" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "sdata.points[\"transcripts_with_morton_codes\"]" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "1b41a4bb-e552-41c7-a0c4-f9b70847d5cb", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'transform': {'global': Scale (x, y, z)\n", - " [4.70588235 4.70588235 1. ]},\n", - " 'spatialdata_attrs': {'feature_key': 'feature_name',\n", - " 'instance_key': 'cell_id'}}" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "sdata.points[\"transcripts_with_morton_codes\"].attrs" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "2035d286", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "aca392bdcba64975bf8f8c088c5e6e49", - "version_major": 2, - "version_minor": 1 - }, - "text/plain": [ - "VitessceWidget(js_dev_mode=True, uid='eed7')" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "vw = (\n", " sdata\n", " .pl.render_images(element=\"morphology_focus\")\n", " .pl.render_shapes(element=\"cell_boundaries\")\n", - " .pl.render_points(element=\"transcripts_with_morton_codes\", color=\"feature_name\", groups=[\"ERBB2\"], palette=[\"red\"])\n", + " .pl.render_points(element=\"transcripts_with_morton_codes\", color=\"feature_name\", groups=[\"ERBB2\"], palette=[\"red\"], table_name=\"table\")\n", " .pl.show()\n", ")\n", "vw" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "5433ec21-e385-4667-a45d-e184611fdde0", + "metadata": {}, + "outputs": [], + "source": [ + "vw.config.to_dict(base_url=\"\")" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/uv.lock b/uv.lock index 2f1c6b3..dcc92c4 100644 --- a/uv.lock +++ b/uv.lock @@ -1085,7 +1085,7 @@ requires-dist = [ { name = "sphinx", marker = "extra == 'docs'" }, { name = "sphinx-book-theme", marker = "extra == 'docs'" }, { name = "sphinx-copybutton", marker = "extra == 'docs'" }, - { name = "vitessce", extras = ["all"], specifier = ">=3.8.0" }, + { name = "vitessce", extras = ["all"], specifier = ">=3.8.1" }, { name = "xarray", specifier = ">=2024.10.0,<=2025.3.0" }, { name = "zarr", specifier = ">=2.5.0,<3" }, ] @@ -4904,7 +4904,7 @@ wheels = [ [[package]] name = "vitessce" -version = "3.8.0" +version = "3.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "black" }, @@ -4917,8 +4917,9 @@ dependencies = [ { name = "zarr", version = "2.18.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "zarr", version = "2.18.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/4a/c8/f5a7928e5521611e1d128d37d336078c9324198929e261524eda15a6559a/vitessce-3.8.1.tar.gz", hash = "sha256:293b99a0b66721368b0500c328f9d7879f6ab08bdce949b749c4a23271d8f397", size = 332200 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/60/b5c82868f136eff8c2d3a8cae675c63b4e9e65b10f6c36861d49aade32ce/vitessce-3.8.0-py3-none-any.whl", hash = "sha256:763f9b8f411d103cd577789acba2baa16b938969826d6ead10dde46f916dda4a", size = 79843 }, + { url = "https://files.pythonhosted.org/packages/d0/8d/cfd9eb3fdc3d37ae12ad3aa4e3b01ea7d886e2941adf212d1be1a7085552/vitessce-3.8.1-py3-none-any.whl", hash = "sha256:7a98683da82d5ffa5b1d7aea3f4a687e7a45090fabded8250895c7912230fbfc", size = 80024 }, ] [package.optional-dependencies] From ea82fb59283c4464827d0ad3e0ad6780ef8191cb Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Thu, 12 Feb 2026 08:50:50 -0500 Subject: [PATCH 7/7] Update --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c08f328..ca6795c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ 'numpy>=1.21.2', 'zarr>=2.5.0,<3', 'numcodecs>=0.5.7,<0.16.0', - 'vitessce[all]>=3.8.1', + 'vitessce[all]>=3.8.2', 'scanpy>=1.11.3', 'anndata>=0.11.4', 'spatialdata>=0.3.0',