Skip to content

Commit 1674e45

Browse files
committed
minor updates to zarr tutorial
1 parent 5856441 commit 1674e45

File tree

1 file changed

+53
-25
lines changed

1 file changed

+53
-25
lines changed

intermediate/intro-to-zarr.ipynb

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
"\n",
1010
"## Learning Objectives:\n",
1111
"\n",
12-
"- Understand the principles of the Zarr file format\n",
13-
"- Learn how to read and write Zarr files using the `zarr-python` library\n",
14-
"- Explore how to use Zarr files with `xarray` for data analysis and visualization\n",
12+
"- Understand the principles of the Zarr data format\n",
13+
"- Learn how to read and write Zarr stores using the `zarr-python` library\n",
14+
"- Explore how to use Zarr stores with `xarray` for data analysis and visualization\n",
1515
"\n",
1616
"This notebook provides a brief introduction to Zarr and how to\n",
1717
"use it in cloud environments for scalable, chunked, and compressed data storage.\n",
1818
"\n",
19-
"Zarr is a file format with implementations in different languages. In this tutorial, we will look at an example of how to use the Zarr format by looking at some features of the `zarr-python` library and how Zarr files can be opened with `xarray`.\n",
19+
"Zarr is a data format with implementations in different languages. In this tutorial, we will look at an example of how to use the Zarr format by looking at some features of the `zarr-python` library and how Zarr files can be opened with `xarray`.\n",
2020
"\n",
2121
"## What is Zarr?\n",
2222
"\n",
@@ -25,17 +25,16 @@
2525
"### Zarr Data Organization:\n",
2626
"- **Arrays**: N-dimensional arrays that can be chunked and compressed.\n",
2727
"- **Groups**: A container for organizing multiple arrays and other groups with a hierarchical structure.\n",
28-
"- **Metadata**: JSON-like metadata describing the arrays and groups, including information about dimensions, data types, groups, and compression.\n",
28+
"- **Metadata**: JSON-like metadata describing the arrays and groups, including information about data types, dimensions, chunking, compression, and user-defined key-value fields. \n",
2929
"- **Dimensions and Shape**: Arrays can have any number of dimensions, and their shape is defined by the number of elements in each dimension.\n",
3030
"- **Coordinates & Indexing**: Zarr supports coordinate arrays for each dimension, allowing for efficient indexing and slicing.\n",
3131
"\n",
32-
"The diagram below from [the NASA Earthdata wiki](https://wiki.earthdata.nasa.gov/display/ESO/Zarr+Format) showing the structure of a Zarr store:\n",
32+
"The diagram below from [the Zarr v3 specification](https://wiki.earthdata.nasa.gov/display/ESO/Zarr+Format) showing the structure of a Zarr store:\n",
3333
"\n",
34-
"![EarthData](https://learning.nceas.ucsb.edu/2025-04-arctic/images/zarr-chunks.png)\n",
34+
"![ZarrSpec](https://zarr-specs.readthedocs.io/en/latest/_images/terminology-hierarchy.excalidraw.png)\n",
3535
"\n",
3636
"\n",
37-
"NetCDF and Zarr share similar terminology and functionality, but the key difference is that NetCDF is a single file, while Zarr is a directory-based “store” composed of many chunked files—making it better suited for distributed and cloud-based workflows.\n",
38-
"\n"
37+
"NetCDF and Zarr share similar terminology and functionality, but the key difference is that NetCDF is a single file, while Zarr is a directory-based “store” composed of many chunked files, making it better suited for distributed and cloud-based workflows."
3938
]
4039
},
4140
{
@@ -69,7 +68,7 @@
6968
"source": [
7069
"import zarr\n",
7170
"\n",
72-
"z = zarr.create(shape=(40, 50), chunks=(10, 10), dtype='f8', store='test.zarr', mode='w')\n",
71+
"z = zarr.create_array(shape=(40, 50), chunks=(10, 10), dtype='f8', store='test.zarr')\n",
7372
"z"
7473
]
7574
},
@@ -228,11 +227,13 @@
228227
"metadata": {},
229228
"outputs": [],
230229
"source": [
231-
"root = zarr.group()\n",
230+
"store = zarr.storage.MemoryStore()\n",
231+
"root = zarr.create_group(store=store)\n",
232232
"temp = root.create_group('temp')\n",
233233
"precip = root.create_group('precip')\n",
234234
"t2m = temp.create_array('t2m', shape=(100, 100), chunks=(10, 10), dtype='i4')\n",
235-
"prcp = precip.create_array('prcp', shape=(1000, 1000), chunks=(10, 10), dtype='i4')"
235+
"prcp = precip.create_array('prcp', shape=(1000, 1000), chunks=(10, 10), dtype='i4')\n",
236+
"root.tree()"
236237
]
237238
},
238239
{
@@ -251,7 +252,7 @@
251252
"metadata": {},
252253
"outputs": [],
253254
"source": [
254-
"root['temp']\n",
255+
"display(root['temp'])\n",
255256
"root['temp/t2m'][:, 3]"
256257
]
257258
},
@@ -281,7 +282,7 @@
281282
"metadata": {},
282283
"outputs": [],
283284
"source": [
284-
"root.tree(expand=True)"
285+
"root.tree()"
285286
]
286287
},
287288
{
@@ -290,7 +291,7 @@
290291
"metadata": {},
291292
"source": [
292293
"#### Chunking\n",
293-
"Chunking is the process of dividing the data arrays into smaller pieces. This allows for parallel processing and efficient storage.\n",
294+
"Chunking is the process of dividing Zarr arrays into smaller pieces. This allows for parallel processing and efficient storage.\n",
294295
"\n",
295296
"One of the important parameters in Zarr is the chunk shape, which determines how the data is divided into smaller, manageable pieces. This is crucial for performance, especially when working with large datasets.\n",
296297
"\n",
@@ -329,7 +330,7 @@
329330
"metadata": {},
330331
"outputs": [],
331332
"source": [
332-
"c = zarr.create(shape=(200, 200, 200), chunks=(1, 200, 200), dtype='f8', store='c.zarr')\n",
333+
"c = zarr.create_array(shape=(200, 200, 200), chunks=(1, 200, 200), dtype='f8', store='c.zarr')\n",
333334
"c[:] = np.random.randn(*c.shape)"
334335
]
335336
},
@@ -350,7 +351,7 @@
350351
"metadata": {},
351352
"outputs": [],
352353
"source": [
353-
"d = zarr.create(shape=(200, 200, 200), chunks=(200, 200, 1), dtype='f8', store='d.zarr')\n",
354+
"d = zarr.create_array(shape=(200, 200, 200), chunks=(200, 200, 1), dtype='f8', store='d.zarr')\n",
354355
"d[:] = np.random.randn(*d.shape)"
355356
]
356357
},
@@ -377,8 +378,12 @@
377378
"- File systems struggle with too many small files.\n",
378379
"- Small files (e.g., 1 MB or less) may waste space due to filesystem block size.\n",
379380
"- Object storage systems (e.g., S3) can slow down with a high number of objects.\n",
381+
"\n",
380382
"With sharding, you choose:\n",
381-
"\n"
383+
"- Shard size: the logical shape of each shard, which is expected to include one or more chunks\n",
384+
"- Chunk size: the shape of each compressed chunk\n",
385+
"\n",
386+
"It is important to remember that the shard is the minimum unit of writing. This means that writers must be able to fit the entire shard (including all of the compressed chunks) in memory before writing a shard to a store.\n"
382387
]
383388
},
384389
{
@@ -526,13 +531,27 @@
526531
"metadata": {},
527532
"outputs": [],
528533
"source": [
534+
"from pprint import pprint\n",
535+
"\n",
529536
"consolidated = zarr.open_group(store=store)\n",
530537
"consolidated_metadata = consolidated.metadata.consolidated_metadata.metadata\n",
531-
"from pprint import pprint\n",
532538
"\n",
533539
"pprint(dict(sorted(consolidated_metadata.items())))"
534540
]
535541
},
542+
{
543+
"cell_type": "markdown",
544+
"id": "a571acec-7a65-4a51-ad1e-c80b17494cd3",
545+
"metadata": {},
546+
"source": [
547+
"Note that while Zarr-Python supports consolidated metadata for v2 and v3 formatted Zarr stores, it is not technically part of the specification (hence the warning above). \n",
548+
"\n",
549+
"⚠️ Use Caution When ⚠️\n",
550+
"- **Stale or incomplete consolidated metadata**: If the dataset is updated but the consolidated metadata entrypoint isn't re-consolidated, readers may miss chunks or metadata. Always run zarr.consolidate_metadata() after changes.\n",
551+
"- **Concurrent writes or multi-writer pipelines**: Consolidated metadata can lead to inconsistent reads if multiple processes write without coordination. Use with caution in dynamic or shared write environments.\n",
552+
"- **Local filesystems or mixed toolchains**: On local storage, consolidation offers little benefit as hierarchy discovery is generally quite cheap. "
553+
]
554+
},
536555
{
537556
"cell_type": "markdown",
538557
"id": "46",
@@ -575,6 +594,8 @@
575594
"metadata": {},
576595
"outputs": [],
577596
"source": [
597+
"import xarray as xr\n",
598+
"\n",
578599
"store = 'https://ncsa.osn.xsede.org/Pangeo/pangeo-forge/gpcp-feedstock/gpcp.zarr'\n",
579600
"\n",
580601
"ds = xr.open_dataset(store, engine='zarr', chunks={}, consolidated=True)\n",
@@ -599,14 +620,13 @@
599620
"::::{admonition} Exercise\n",
600621
":class: tip\n",
601622
"\n",
602-
"Can you calculate the mean precipitation over the time dimension in the GPCP dataset and plot it?\n",
623+
"Can you calculate the mean precipitation for January 2020 in the GPCP dataset and plot it?\n",
603624
"\n",
604625
":::{admonition} Solution\n",
605626
":class: dropdown\n",
606627
"\n",
607628
"```python\n",
608-
"ds.precip.mean(dim='time').plot()\n",
609-
"\n",
629+
"ds.precip.sel(time=slice('2020-01-01', '2020-01-31')).mean(dim='time').plot()\n",
610630
"```\n",
611631
":::\n",
612632
"::::"
@@ -628,13 +648,20 @@
628648
]
629649
},
630650
{
631-
"cell_type": "markdown",
632-
"id": "53",
651+
"cell_type": "code",
652+
"execution_count": null,
653+
"id": "09c50842-b522-4f3f-b04a-da22f9131b86",
633654
"metadata": {},
655+
"outputs": [],
634656
"source": []
635657
}
636658
],
637659
"metadata": {
660+
"kernelspec": {
661+
"display_name": "Python 3 (ipykernel)",
662+
"language": "python",
663+
"name": "python3"
664+
},
638665
"language_info": {
639666
"codemirror_mode": {
640667
"name": "ipython",
@@ -644,7 +671,8 @@
644671
"mimetype": "text/x-python",
645672
"name": "python",
646673
"nbconvert_exporter": "python",
647-
"pygments_lexer": "ipython3"
674+
"pygments_lexer": "ipython3",
675+
"version": "3.12.11"
648676
}
649677
},
650678
"nbformat": 4,

0 commit comments

Comments
 (0)