Skip to content

Commit 624db76

Browse files
Use zarr3 and unpin dask (#486)
* use zarr3 and unpin dask * relax ome-zarr upped bound * allow-direct-references hatch * set tool.hatch.metadata.allow-direct-references false * WIP: support for zarr v3 * WIP: fix tests * Remove storage of explicit bounding_box metadata * Lint * Pin snakemake * Lint * F-string issue * Undo pin snakemake * Update xenium morton code nb * Updates --------- Co-authored-by: Mark Keller <7525285+keller-mark@users.noreply.github.com>
1 parent ef0ecf5 commit 624db76

15 files changed

Lines changed: 433 additions & 223 deletions

.python-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.10
1+
3.12

demos/satija-2020/Snakefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ rule all:
2727
rule convert_to_zarr:
2828
input:
2929
cells_h5ad=(RAW_DIR / "uf-processed" / GLOBUS_ID / "cluster_marker_genes.h5ad"),
30-
annotations_csv=(RAW_DIR / "annotations_spleen" / f"{GLOBUS_ID}.csv"),
30+
# For some reasons f-strings in python 3.12 don't work as expected
31+
# Reference: https://github.com/NeuralEnsemble/cobrawap/pull/105
32+
annotations_csv=(RAW_DIR / "annotations_spleen" / str(GLOBUS_ID + ".csv")),
3133
cl_obo=(RAW_DIR / "cl.obo")
3234
output:
3335
cells=directory(PROCESSED_DIR / "satija_2020.h5ad.zarr"),
@@ -100,4 +102,4 @@ rule download_cl_obo:
100102
shell:
101103
'''
102104
curl -L -o {output} {params.file_url}
103-
'''
105+
'''

docs/data_options.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,18 @@ This is currently supported for the ``AnnDataWrapper`` class using the ``adata_s
129129
vc.widget()
130130
131131
132-
Or, with a Zarr store instance (instead of a local string path to a DirectoryStore):
132+
Or, with a Zarr store instance (instead of a local string path to a LocalStore/DirectoryStore):
133133

134134
.. code-block:: python
135135
136136
import zarr
137+
from obstore.store import HTTPStore
137138
from vitessce import VitessceConfig, AnnDataWrapper
138139
139-
# ...
140-
store = zarr.storage.FSStore("s3://my_bucket/path/to/my_store.adata.zarr")
140+
obs_store = HTTPStore.from_url("https://my_bucket.example.com/path/to/my_store.adata.zarr")
141+
store = zarr.storage.ObjectStore(obs_store, read_only=True)
142+
# or
143+
store = zarr.storage.LocalStore("./path/to/my_store.adata.zarr")
141144
142145
vc = VitessceConfig(name="My Vitessce Configuration")
143146
vc.add_dataset(name="My Dataset").add_object(AnnDataWrapper(

docs/notebooks/spatial_data_blobs.ipynb

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
")\n",
116116
"# Add data to the configuration:\n",
117117
"wrapper = SpatialDataWrapper(\n",
118-
" sdata_path=spatialdata_filepath,\n",
118+
" sdata_store=spatialdata_filepath,\n",
119119
" # The following paths are relative to the root of the SpatialData zarr store on-disk.\n",
120120
" image_path=\"images/blobs_image\",\n",
121121
" obs_segmentations_path=\"labels/blobs_labels\",\n",
@@ -129,7 +129,7 @@
129129
" }\n",
130130
")\n",
131131
"points_wrapper = SpatialDataWrapper(\n",
132-
" sdata_path=spatialdata_filepath,\n",
132+
" sdata_store=spatialdata_filepath,\n",
133133
" # The following paths are relative to the root of the SpatialData zarr store on-disk.\n",
134134
" obs_points_path=\"points/blobs_points\",\n",
135135
" obs_feature_matrix_path=\"tables/table_points/X\", # TODO\n",
@@ -221,6 +221,27 @@
221221
"outputs": [],
222222
"source": []
223223
},
224+
{
225+
"cell_type": "code",
226+
"execution_count": null,
227+
"metadata": {},
228+
"outputs": [],
229+
"source": []
230+
},
231+
{
232+
"cell_type": "code",
233+
"execution_count": null,
234+
"metadata": {},
235+
"outputs": [],
236+
"source": []
237+
},
238+
{
239+
"cell_type": "code",
240+
"execution_count": null,
241+
"metadata": {},
242+
"outputs": [],
243+
"source": []
244+
},
224245
{
225246
"cell_type": "code",
226247
"execution_count": null,
@@ -245,7 +266,7 @@
245266
"name": "python",
246267
"nbconvert_exporter": "python",
247268
"pygments_lexer": "ipython3",
248-
"version": "3.10.14"
269+
"version": "3.12.5"
249270
}
250271
},
251272
"nbformat": 4,

docs/notebooks/spatial_data_xenium_morton.ipynb

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
"from vitessce.data_utils import (\n",
4848
" sdata_morton_sort_points,\n",
4949
" sdata_points_process_columns,\n",
50-
" sdata_points_write_bounding_box_attrs,\n",
5150
" sdata_points_modify_row_group_size,\n",
5251
" sdata_morton_query_rect,\n",
5352
")"
@@ -59,7 +58,8 @@
5958
"metadata": {},
6059
"outputs": [],
6160
"source": [
62-
"from spatialdata import read_zarr"
61+
"from spatialdata import read_zarr\n",
62+
"from spatialdata.models import PointsModel"
6363
]
6464
},
6565
{
@@ -82,7 +82,7 @@
8282
"if not isdir(spatialdata_filepath):\n",
8383
" if not isfile(zip_filepath):\n",
8484
" os.makedirs(data_dir, exist_ok=True)\n",
85-
" urlretrieve('https://s3.embl.de/spatialdata/spatialdata-sandbox/xenium_rep1_io.zip', zip_filepath)\n",
85+
" urlretrieve('https://data-2.vitessce.io/sdata-datasets/xenium_rep1_io.spatialdata.zarr.zip', zip_filepath)\n",
8686
" with zipfile.ZipFile(zip_filepath,\"r\") as zip_ref:\n",
8787
" zip_ref.extractall(data_dir)\n",
8888
" os.rename(join(data_dir, \"data.zarr\"), spatialdata_filepath)\n",
@@ -161,6 +161,15 @@
161161
"sdata.points['transcripts'].head()"
162162
]
163163
},
164+
{
165+
"cell_type": "code",
166+
"execution_count": null,
167+
"metadata": {},
168+
"outputs": [],
169+
"source": [
170+
"transformations = sdata['transcripts'].attrs['transform']"
171+
]
172+
},
164173
{
165174
"cell_type": "markdown",
166175
"metadata": {},
@@ -223,15 +232,7 @@
223232
"metadata": {},
224233
"outputs": [],
225234
"source": [
226-
"sdata[\"transcripts_with_morton_codes\"] = ddf\n",
227-
"sdata.write_element(\"transcripts_with_morton_codes\")"
228-
]
229-
},
230-
{
231-
"cell_type": "markdown",
232-
"metadata": {},
233-
"source": [
234-
"### Step 4. Write bounding box metadata with `sdata_points_write_bounding_box_attrs`"
235+
"del ddf.attrs['transform']"
235236
]
236237
},
237238
{
@@ -240,14 +241,15 @@
240241
"metadata": {},
241242
"outputs": [],
242243
"source": [
243-
"sdata_points_write_bounding_box_attrs(sdata, \"transcripts_with_morton_codes\")"
244+
"sdata[\"transcripts_with_morton_codes\"] = PointsModel.parse(ddf, feature_key=\"feature_name\", instance_key=\"cell_id\", transformations=transformations)\n",
245+
"sdata.write_element(\"transcripts_with_morton_codes\")"
244246
]
245247
},
246248
{
247249
"cell_type": "markdown",
248250
"metadata": {},
249251
"source": [
250-
"### Step 5. Modify the row group sizes of the Parquet files with `sdata_points_modify_row_group_size`"
252+
"### Step 4. Modify the row group sizes of the Parquet files with `sdata_points_modify_row_group_size`"
251253
]
252254
},
253255
{
@@ -278,7 +280,7 @@
278280
"import pyarrow.parquet as pq\n",
279281
"from os.path import join\n",
280282
"\n",
281-
"parquet_file = pq.ParquetFile(join(sdata.path, \"points\", \"transcripts_with_morton_codes\", \"points.parquet\", \"part.0.parquet\"))\n",
283+
"parquet_file = pq.ParquetFile(join(sdata.path, \"points\", \"transcripts_with_morton_codes\", \"points.parquet\", \"part.1.parquet\"))\n",
282284
"\n",
283285
"# Get the number of row groups in this part-0 file.\n",
284286
"num_groups = parquet_file.num_row_groups\n",
@@ -308,29 +310,57 @@
308310
"wrapper = SpatialDataWrapper(\n",
309311
" sdata_path=spatialdata_filepath,\n",
310312
" # The following paths are relative to the root of the SpatialData zarr store on-disk.\n",
311-
" image_path=\"images/rasterized\",\n",
313+
" image_path=\"images/morphology_focus\",\n",
312314
" table_path=\"tables/table\",\n",
313315
" obs_feature_matrix_path=\"tables/table/X\",\n",
314-
" obs_spots_path=\"shapes/cells\",\n",
316+
" #obs_spots_path=\"shapes/cell_circles\",\n",
317+
" obs_segmentations_path=\"shapes/cell_boundaries\",\n",
315318
" coordinate_system=\"global\",\n",
316319
" coordination_values={\n",
317320
" # The following tells Vitessce to consider each observation as a \"spot\"\n",
318321
" \"obsType\": \"cell\",\n",
319322
" }\n",
320323
")\n",
321-
"dataset = vc.add_dataset(name='MERFISH').add_object(wrapper)\n",
324+
"points_wrapper = SpatialDataWrapper(\n",
325+
" sdata_path=spatialdata_filepath,\n",
326+
" # The following paths are relative to the root of the SpatialData zarr store on-disk.\n",
327+
" obs_points_path=\"points/transcripts_with_morton_codes\",\n",
328+
" obs_feature_matrix_path=\"tables/dense_table/X\",\n",
329+
" coordinate_system=\"global\",\n",
330+
" coordination_values={\n",
331+
" \"obsType\": \"point\",\n",
332+
" \"featureType\": \"gene\",\n",
333+
" }\n",
334+
")\n",
335+
"dataset = vc.add_dataset(name='MERFISH').add_object(wrapper).add_object(points_wrapper)\n",
322336
"\n",
323337
"# Add views (visualizations) to the configuration:\n",
324338
"spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n",
325339
"feature_list = vc.add_view(\"featureList\", dataset=dataset)\n",
326340
"layer_controller = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n",
327341
"obs_sets = vc.add_view(\"obsSets\", dataset=dataset)\n",
328342
"\n",
343+
"\"\"\"\n",
329344
"vc.link_views_by_dict([spatial, layer_controller], {\n",
330345
" 'spotLayer': CL([{\n",
331346
" 'obsType': 'cell',\n",
332347
" }]),\n",
333348
"}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSpots\"))\n",
349+
"\"\"\"\n",
350+
"\n",
351+
"vc.link_views_by_dict([spatial, layer_controller], {\n",
352+
" 'segmentationLayer': CL([{\n",
353+
" 'segmentationChannel': CL([{\n",
354+
" 'obsType': 'cell',\n",
355+
" }]),\n",
356+
" }]),\n",
357+
"}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n",
358+
"\n",
359+
"vc.link_views_by_dict([spatial, layer_controller], {\n",
360+
" 'pointLayer': CL([{\n",
361+
" 'obsType': 'point',\n",
362+
" }]),\n",
363+
"}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsPoints\"))\n",
334364
"\n",
335365
"vc.link_views([spatial, layer_controller, feature_list, obs_sets], ['obsType'], [wrapper.obs_type_label])\n",
336366
"\n",
@@ -379,7 +409,7 @@
379409
"name": "python",
380410
"nbconvert_exporter": "python",
381411
"pygments_lexer": "ipython3",
382-
"version": "3.10.14"
412+
"version": "3.12.5"
383413
}
384414
},
385415
"nbformat": 4,

pyproject.toml

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "vitessce"
7-
version = "3.8.4"
7+
version = "3.9.0"
88
authors = [
99
{ name="Mark Keller", email="mark_keller@hms.harvard.edu" },
1010
]
1111
description = "Jupyter widget facilitating interactive visualization of spatial single-cell data with Vitessce"
1212
readme = "README.md"
1313
license = {file = "LICENSE"}
14-
requires-python = ">=3.10"
14+
requires-python = ">=3.12"
1515
keywords = ["ipython", "jupyter", "widgets"]
1616
classifiers = [
1717
'Development Status :: 4 - Beta',
@@ -29,8 +29,7 @@ dependencies = [
2929
'pandas>=1.1.2',
3030
'black>=21.11b1',
3131
'numpy>=1.21.2',
32-
'zarr>=2.5.0,<3',
33-
'numcodecs>=0.5.7,<0.16.0',
32+
'zarr>=3.0.0',
3433
]
3534

3635
[project.optional-dependencies]
@@ -61,22 +60,22 @@ docs = [
6160
all = [
6261
# For data_utils
6362
'negspy>=0.2.24',
64-
'anndata>=0.7.8',
63+
'anndata>=0.12.10',
6564
# scanpy < 1.10.3 does not support numpy >= 2.0.0 and does not
6665
# Reference: https://github.com/scverse/scanpy/pull/3115/files
6766
'scanpy>=1.10.2',
68-
'ome-zarr<0.10.3',
67+
'ome-zarr>=0.12.2',
6968
'tifffile>=2020.10.1',
7069

7170
'jupyter-server-proxy>=1.5.2',
7271
'oxc-py>=0.1.1',
73-
'anywidget>=0.9.10',
72+
'anywidget>=0.11.0',
7473
'uvicorn>=0.17.0',
7574
'ujson>=4.0.1',
7675
'starlette>=0.14.0',
7776
'generate-tiff-offsets>=0.1.9',
7877
'kerchunk>=0.2.6',
79-
'fsspec',
78+
'obstore',
8079

8180
# aiofiles is not explicitly referenced in our code,
8281
# but it is an implicit dependency of starlette==0.14.0.
@@ -88,8 +87,7 @@ building = []
8887
testing = []
8988
linting = []
9089
notebook = [
91-
'spatialdata>=0.3.0',
92-
'dask[dataframe]==2024.11.1',
90+
'spatialdata>=0.7.3',
9391
'marimo',
9492
'starlette>=0.42.0',
9593
'tqdm>=4.1.0',
@@ -121,8 +119,7 @@ dev = [
121119
'boto3>=1.16.30',
122120
'scikit-misc>=0.1.3',
123121
'autopep8>=2.0.2',
124-
'spatialdata>=0.3.0',
125-
'dask[dataframe]==2024.11.1',
122+
'spatialdata>=0.7.3',
126123
]
127124

128125
[tool.uv]

src/vitessce/data_utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
sdata_morton_sort_points,
2323
# Other helper functions
2424
sdata_points_process_columns,
25-
sdata_points_write_bounding_box_attrs,
2625
sdata_points_modify_row_group_size,
2726
# Functions for querying
2827
sdata_morton_query_rect,
2928
row_ranges_to_row_indices,
29+
MORTON_CODE_EXTREME_VALUE_INDICATOR,
3030
)

src/vitessce/data_utils/entities.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,6 @@ def __init__(self, f, profile_paths, assembly='hg38', starting_resolution=5000,
196196

197197
num_profiles = len(profile_paths)
198198

199-
compressor = 'default'
200-
201199
chromosomes = [str(chr_name) for chr_name in nc.get_chromorder(
202200
assembly)[:25]] # TODO: should more than chr1-chrM be used?
203201
chroms_length_arr = np.array(
@@ -217,8 +215,8 @@ def __init__(self, f, profile_paths, assembly='hg38', starting_resolution=5000,
217215
# Create each resolution group.
218216
for resolution in resolutions:
219217
chr_shape = (num_profiles, math.ceil(chr_len / resolution))
220-
chr_group.create_dataset(str(
221-
resolution), shape=chr_shape, dtype="f4", fill_value=np.nan, compressor=compressor)
218+
chr_group.create_array(str(
219+
resolution), shape=chr_shape, dtype="f4", fill_value=np.nan)
222220

223221
# f.attrs should contain the properties required for HiGlass's "tileset_info" requests.
224222
f.attrs['row_infos'] = [

0 commit comments

Comments
 (0)