Kerchunk and Xarray-Datatree at Scale
Overview
In this tutorial we are going to use a large collection of pre-generated Kerchunk
reference files and open them with xarray-datatree. This chapter is heavily inspired by this blog post.
About the Dataset
This collection of reference files were generated from the NASA NEX-GDDP-CMIP6 (Global Daily Downscaled Projections) dataset. A version of this dataset is hosted on s3
as a collection of NetCDF files.
Prerequisites
Concepts |
Importance |
Notes |
---|---|---|
Kerchunk Basics |
Required |
Core |
Multiple Files and Kerchunk |
Required |
Core |
Kerchunk and Dask |
Required |
Core |
Multi-File Datasets with Kerchunk |
Required |
IO/Visualization |
Required |
IO |
Time to learn: 30 minutes
Motivation
In total the dataset is roughly 12TB in compressed blob storage, with a single NetCDF
file per yearly timestep, per variable. Downloading this entire dataset for analysis on a local machine would difficult to say the least. The collection of Kerchunk
reference files for this entire dataset is only 272 Mb, which is about 42,000 times smaller!
Imports
from datatree import DataTree
import xarray as xr
import pandas as pd
import dask
from distributed import Client
from fsspec.implementations.reference import ReferenceFileSystem
import hvplot.xarray
Read the Reference Catalog
The NASA NEX-GDDP-CMIP6 dataset is organized by GCM, Scenario and Ensemble Member. Each of these Scenario/GCM combinations is represented as a combined reference file, which was created by merging across variables and concatenating along time-steps. All of these references are organized into a simple .csv
catalog in the schema:
GCM/Scenario |
url |
---|
Organzing with Xarray-Datatree
Not all of the GCM/Scenario reference datasets have shared spatial coordinates and many of the have slight differences in their calendar and thus time dimension.
Because of this, these cannot be combined into a single Xarray-Dataset
. Fortunately Xarray-Datatree
provides a higher level abstraction where related Xarray-Datasets
are organized into a tree structure where each dataset corresponds to a leaf
.
# Read the reference catalog into a Pandas DataFrame
cat_df = pd.read_csv(
"s3://carbonplan-share/nasa-nex-reference/reference_catalog_nested.csv"
)
# Convert the DataFrame into a dictionary
catalog = cat_df.set_index("ID").T.to_dict("records")[0]
Load Reference Datasets into Xarray-DataTree
In the following cell we create a function load_ref_ds
, which can be parallelized via Dask
to load Kerchunk
references into a dictionary of Xarray-Datasets
.
def load_ref_ds(url: str):
fs = ReferenceFileSystem(
url,
remote_protocol="s3",
target_protocol="s3",
target_options={"anon": True},
lazy=True,
)
return xr.open_dataset(
fs.get_mapper(),
engine="zarr",
backend_kwargs={"consolidated": False},
chunks={"time": 300},
)
tasks = {id: dask.delayed(load_ref_ds)(url) for id, url in catalog.items()}
Use Dask Distributed to load the Xarray-Datasets from Kerchunk reference files
Using Dask
, we are loading 164 reference datasets into memory. Since they are are Xarray
datasets the coordinates are loaded eagerly, but the underlying data is still lazy.
client = Client(n_workers=8)
client
Client
Client-cf5b0644-67af-11ee-8ac0-00224808823d
Connection method: Cluster object | Cluster type: distributed.LocalCluster |
Dashboard: http://127.0.0.1:8787/status |
Cluster Info
LocalCluster
1d81b49f
Dashboard: http://127.0.0.1:8787/status | Workers: 8 |
Total threads: 8 | Total memory: 6.76 GiB |
Status: running | Using processes: True |
Scheduler Info
Scheduler
Scheduler-e5c7235c-1b9a-4b36-8438-65f7c746fe85
Comm: tcp://127.0.0.1:45263 | Workers: 8 |
Dashboard: http://127.0.0.1:8787/status | Total threads: 8 |
Started: Just now | Total memory: 6.76 GiB |
Workers
Worker: 0
Comm: tcp://127.0.0.1:40403 | Total threads: 1 |
Dashboard: http://127.0.0.1:42861/status | Memory: 865.25 MiB |
Nanny: tcp://127.0.0.1:44091 | |
Local directory: /tmp/dask-scratch-space/worker-dog61tph |
Worker: 1
Comm: tcp://127.0.0.1:42449 | Total threads: 1 |
Dashboard: http://127.0.0.1:35057/status | Memory: 865.25 MiB |
Nanny: tcp://127.0.0.1:35661 | |
Local directory: /tmp/dask-scratch-space/worker-xiof6d_c |
Worker: 2
Comm: tcp://127.0.0.1:43097 | Total threads: 1 |
Dashboard: http://127.0.0.1:41407/status | Memory: 865.25 MiB |
Nanny: tcp://127.0.0.1:46507 | |
Local directory: /tmp/dask-scratch-space/worker-by9zbbx3 |
Worker: 3
Comm: tcp://127.0.0.1:34729 | Total threads: 1 |
Dashboard: http://127.0.0.1:35879/status | Memory: 865.25 MiB |
Nanny: tcp://127.0.0.1:39985 | |
Local directory: /tmp/dask-scratch-space/worker-ttudeyor |
Worker: 4
Comm: tcp://127.0.0.1:34477 | Total threads: 1 |
Dashboard: http://127.0.0.1:40377/status | Memory: 865.25 MiB |
Nanny: tcp://127.0.0.1:34601 | |
Local directory: /tmp/dask-scratch-space/worker-grvuiljw |
Worker: 5
Comm: tcp://127.0.0.1:35361 | Total threads: 1 |
Dashboard: http://127.0.0.1:42941/status | Memory: 865.25 MiB |
Nanny: tcp://127.0.0.1:38231 | |
Local directory: /tmp/dask-scratch-space/worker-5kz4auvw |
Worker: 6
Comm: tcp://127.0.0.1:46373 | Total threads: 1 |
Dashboard: http://127.0.0.1:40495/status | Memory: 865.25 MiB |
Nanny: tcp://127.0.0.1:42133 | |
Local directory: /tmp/dask-scratch-space/worker-30x2x_za |
Worker: 7
Comm: tcp://127.0.0.1:33703 | Total threads: 1 |
Dashboard: http://127.0.0.1:36563/status | Memory: 865.25 MiB |
Nanny: tcp://127.0.0.1:39815 | |
Local directory: /tmp/dask-scratch-space/worker-2jiifgn5 |
catalog_computed = dask.compute(tasks)
Build an Xarray-Datatree from the dictionary of datasets
dt = DataTree.from_dict(catalog_computed[0])
Accessing the Datatree
A Datatree
is a collection of related Xarray
datasets. We can access individual datasets using UNIX
syntax. In the cell below, we will access a single dataset from the datatree
.
dt["ACCESS-CM2/ssp585"]
# or
dt["ACCESS-CM2"]["ssp585"]
<xarray.DatasetView> Dimensions: (time: 31411, lat: 600, lon: 1440) Coordinates: * lat (lat) float64 6.912e-310 6.912e-310 6.912e-310 ... 0.0 0.0 0.0 * lon (lon) float64 6.912e-310 6.912e-310 6.912e-310 ... 0.0 0.0 0.0 * time (time) datetime64[ns] 2015-01-01T12:00:00 ... 2100-12-31T12:00:00 Data variables: hurs (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> huss (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> pr (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> rlds (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> rsds (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> sfcWind (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> tas (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> tasmax (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> tasmin (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> Attributes: (12/22) Conventions: CF-1.7 activity: NEX-GDDP-CMIP6 cmip6_institution_id: CSIRO-ARCCSS cmip6_license: CC-BY-SA 4.0 cmip6_source_id: ACCESS-CM2 contact: Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget... ... ... scenario: ssp585 source: BCSD title: ACCESS-CM2, r1i1p1f1, ssp585, global downscaled CM... tracking_id: dcae752c-5de5-4170-8a98-e7043538d702 variant_label: r1i1p1f1 version: 1.0
Convert a Datatree node to a Dataset
dt["ACCESS-CM2"]["ssp585"].to_dataset()
<xarray.Dataset> Dimensions: (time: 31411, lat: 600, lon: 1440) Coordinates: * lat (lat) float64 6.912e-310 6.912e-310 6.912e-310 ... 0.0 0.0 0.0 * lon (lon) float64 6.912e-310 6.912e-310 6.912e-310 ... 0.0 0.0 0.0 * time (time) datetime64[ns] 2015-01-01T12:00:00 ... 2100-12-31T12:00:00 Data variables: hurs (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> huss (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> pr (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> rlds (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> rsds (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> sfcWind (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> tas (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> tasmax (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> tasmin (time, lat, lon) float32 dask.array<chunksize=(300, 600, 1440), meta=np.ndarray> Attributes: (12/22) Conventions: CF-1.7 activity: NEX-GDDP-CMIP6 cmip6_institution_id: CSIRO-ARCCSS cmip6_license: CC-BY-SA 4.0 cmip6_source_id: ACCESS-CM2 contact: Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget... ... ... scenario: ssp585 source: BCSD title: ACCESS-CM2, r1i1p1f1, ssp585, global downscaled CM... tracking_id: dcae752c-5de5-4170-8a98-e7043538d702 variant_label: r1i1p1f1 version: 1.0
Operations across a Datatree
A Datatree
contains a collection of datasets with related coordinates and variables. Using some in-built methods, we can analyze it as if it were a single dataset. Instead of looping through hundreds of Xarray
datasets, we can apply operations across the Datatree
. In the example below, we will lazily create a time-series.
ts = dt.mean(dim=["lat", "lon"])
Visualize a single dataset with HvPlot
display(dt["ACCESS-CM2/ssp585"].to_dataset().pr.hvplot("lon", "lat", rasterize=True))
/usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/util.py:2062: RuntimeWarning: overflow encountered in scalar divide
return length/diff
/usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/sheetcoords.py:232: RuntimeWarning: invalid value encountered in scalar multiply
float_col = (x-self.lbrt[0]) * xdensity
WARNING:param.dynamic_operation: Callable raised "ValueError('cannot convert float NaN to integer')".
Invoked as dynamic_operation(numpy.datetime64('2015-01-01T12:00:00.000000000'))
WARNING:param.dynamic_operation: Callable raised "ValueError('cannot convert float NaN to integer')".
Invoked as dynamic_operation(numpy.datetime64('2015-01-01T12:00:00.000000000'), height=300, scale=1.0, width=700, x_range=None, y_range=None)
WARNING:param.dynamic_operation: Callable raised "ValueError('cannot convert float NaN to integer')".
Invoked as dynamic_operation(numpy.datetime64('2015-01-01T12:00:00.000000000'), height=300, scale=1.0, width=700, x_range=None, y_range=None)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/IPython/core/formatters.py:974, in MimeBundleFormatter.__call__(self, obj, include, exclude)
971 method = get_real_method(obj, self.print_method)
973 if method is not None:
--> 974 return method(include=include, exclude=exclude)
975 return None
976 else:
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/dimension.py:1287, in Dimensioned._repr_mimebundle_(self, include, exclude)
1280 def _repr_mimebundle_(self, include=None, exclude=None):
1281 """
1282 Resolves the class hierarchy for the class rendering the
1283 object using any display hooks registered on Store.display
1284 hooks. The output of all registered display_hooks is then
1285 combined and returned.
1286 """
-> 1287 return Store.render(self)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/options.py:1423, in Store.render(cls, obj)
1421 data, metadata = {}, {}
1422 for hook in hooks:
-> 1423 ret = hook(obj)
1424 if ret is None:
1425 continue
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:280, in pprint_display(obj)
278 if not ip.display_formatter.formatters['text/plain'].pprint:
279 return None
--> 280 return display(obj, raw_output=True)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:254, in display(obj, raw_output, **kwargs)
252 elif isinstance(obj, (HoloMap, DynamicMap)):
253 with option_state(obj):
--> 254 output = map_display(obj)
255 elif isinstance(obj, Plot):
256 output = render(obj)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:142, in display_hook.<locals>.wrapped(element)
140 try:
141 max_frames = OutputSettings.options['max_frames']
--> 142 mimebundle = fn(element, max_frames=max_frames)
143 if mimebundle is None:
144 return {}, {}
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:202, in map_display(vmap, max_frames)
199 max_frame_warning(max_frames)
200 return None
--> 202 return render(vmap)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:69, in render(obj, **kwargs)
66 if renderer.fig == 'pdf':
67 renderer = renderer.instance(fig='png')
---> 69 return renderer.components(obj, **kwargs)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/plotting/renderer.py:397, in Renderer.components(self, obj, fmt, comm, **kwargs)
394 embed = (not (dynamic or streams or self.widget_mode == 'live') or config.embed)
396 if embed or config.comms == 'default':
--> 397 return self._render_panel(plot, embed, comm)
398 return self._render_ipywidget(plot)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/plotting/renderer.py:404, in Renderer._render_panel(self, plot, embed, comm)
402 doc = Document()
403 with config.set(embed=embed):
--> 404 model = plot.layout._render_model(doc, comm)
405 if embed:
406 return render_model(model, comm)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/panel/viewable.py:737, in Viewable._render_model(self, doc, comm)
735 if comm is None:
736 comm = state._comm_manager.get_server_comm()
--> 737 model = self.get_root(doc, comm)
739 if self._design and self._design.theme.bokeh_theme:
740 doc.theme = self._design.theme.bokeh_theme
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/panel/layout/base.py:306, in Panel.get_root(self, doc, comm, preprocess)
302 def get_root(
303 self, doc: Optional[Document] = None, comm: Optional[Comm] = None,
304 preprocess: bool = True
305 ) -> Model:
--> 306 root = super().get_root(doc, comm, preprocess)
307 # ALERT: Find a better way to handle this
308 if hasattr(root, 'styles') and 'overflow-x' in root.styles:
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/panel/viewable.py:659, in Renderable.get_root(self, doc, comm, preprocess)
657 wrapper = self._design._wrapper(self)
658 if wrapper is self:
--> 659 root = self._get_model(doc, comm=comm)
660 if preprocess:
661 self._preprocess(root)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/panel/layout/base.py:174, in Panel._get_model(self, doc, root, parent, comm)
172 root = root or model
173 self._models[root.ref['id']] = (model, parent)
--> 174 objects, _ = self._get_objects(model, [], doc, root, comm)
175 props = self._get_properties(doc)
176 props[self._property_mapping['objects']] = objects
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/panel/layout/base.py:156, in Panel._get_objects(self, model, old_objects, doc, root, comm)
154 else:
155 try:
--> 156 child = pane._get_model(doc, root, model, comm)
157 except RerenderError as e:
158 if e.layout is not None and e.layout is not self:
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/panel/pane/holoviews.py:411, in HoloViews._get_model(self, doc, root, parent, comm)
409 plot = self.object
410 else:
--> 411 plot = self._render(doc, comm, root)
413 plot.pane = self
414 backend = plot.renderer.backend
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/panel/pane/holoviews.py:506, in HoloViews._render(self, doc, comm, root)
503 if comm:
504 kwargs['comm'] = comm
--> 506 return renderer.get_plot(self.object, **kwargs)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/plotting/bokeh/renderer.py:70, in BokehRenderer.get_plot(self_or_cls, obj, doc, renderer, **kwargs)
63 @bothmethod
64 def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs):
65 """
66 Given a HoloViews Viewable return a corresponding plot instance.
67 Allows supplying a document attach the plot to, useful when
68 combining the bokeh model with another plot.
69 """
---> 70 plot = super().get_plot(obj, doc, renderer, **kwargs)
71 if plot.document is None:
72 plot.document = Document() if self_or_cls.notebook_context else curdoc()
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/plotting/renderer.py:218, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
215 raise SkipRendering(msg.format(dims=dims))
217 # Initialize DynamicMaps with first data item
--> 218 initialize_dynamic(obj)
220 if not renderer:
221 renderer = self_or_cls
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/plotting/util.py:256, in initialize_dynamic(obj)
254 continue
255 if not len(dmap):
--> 256 dmap[dmap._initial_key()]
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:1212, in DynamicMap.__getitem__(self, key)
1210 # Not a cross product and nothing cached so compute element.
1211 if cache is not None: return cache
-> 1212 val = self._execute_callback(*tuple_key)
1213 if data_slice:
1214 val = self._dataslice(val, data_slice)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:979, in DynamicMap._execute_callback(self, *args)
976 kwargs['_memoization_hash_'] = hash_items
978 with dynamicmap_memoization(self.callback, self.streams):
--> 979 retval = self.callback(*args, **kwargs)
980 return self._style(retval)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:579, in Callable.__call__(self, *args, **kwargs)
576 args, kwargs = (), dict(pos_kwargs, **kwargs)
578 try:
--> 579 ret = self.callable(*args, **kwargs)
580 except KeyError:
581 # KeyError is caught separately because it is used to signal
582 # invalid keys on DynamicMap and should not warn
583 raise
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/util/__init__.py:1024, in Dynamic._dynamic_operation.<locals>.dynamic_operation(*key, **kwargs)
1023 def dynamic_operation(*key, **kwargs):
-> 1024 key, obj = resolve(key, kwargs)
1025 return apply(obj, *key, **kwargs)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/util/__init__.py:1013, in Dynamic._dynamic_operation.<locals>.resolve(key, kwargs)
1011 elif isinstance(map_obj, DynamicMap) and map_obj._posarg_keys and not key:
1012 key = tuple(kwargs[k] for k in map_obj._posarg_keys)
-> 1013 return key, map_obj[key]
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:1212, in DynamicMap.__getitem__(self, key)
1210 # Not a cross product and nothing cached so compute element.
1211 if cache is not None: return cache
-> 1212 val = self._execute_callback(*tuple_key)
1213 if data_slice:
1214 val = self._dataslice(val, data_slice)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:979, in DynamicMap._execute_callback(self, *args)
976 kwargs['_memoization_hash_'] = hash_items
978 with dynamicmap_memoization(self.callback, self.streams):
--> 979 retval = self.callback(*args, **kwargs)
980 return self._style(retval)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:579, in Callable.__call__(self, *args, **kwargs)
576 args, kwargs = (), dict(pos_kwargs, **kwargs)
578 try:
--> 579 ret = self.callable(*args, **kwargs)
580 except KeyError:
581 # KeyError is caught separately because it is used to signal
582 # invalid keys on DynamicMap and should not warn
583 raise
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/util/__init__.py:1024, in Dynamic._dynamic_operation.<locals>.dynamic_operation(*key, **kwargs)
1023 def dynamic_operation(*key, **kwargs):
-> 1024 key, obj = resolve(key, kwargs)
1025 return apply(obj, *key, **kwargs)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/util/__init__.py:1013, in Dynamic._dynamic_operation.<locals>.resolve(key, kwargs)
1011 elif isinstance(map_obj, DynamicMap) and map_obj._posarg_keys and not key:
1012 key = tuple(kwargs[k] for k in map_obj._posarg_keys)
-> 1013 return key, map_obj[key]
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:1212, in DynamicMap.__getitem__(self, key)
1210 # Not a cross product and nothing cached so compute element.
1211 if cache is not None: return cache
-> 1212 val = self._execute_callback(*tuple_key)
1213 if data_slice:
1214 val = self._dataslice(val, data_slice)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:979, in DynamicMap._execute_callback(self, *args)
976 kwargs['_memoization_hash_'] = hash_items
978 with dynamicmap_memoization(self.callback, self.streams):
--> 979 retval = self.callback(*args, **kwargs)
980 return self._style(retval)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/spaces.py:579, in Callable.__call__(self, *args, **kwargs)
576 args, kwargs = (), dict(pos_kwargs, **kwargs)
578 try:
--> 579 ret = self.callable(*args, **kwargs)
580 except KeyError:
581 # KeyError is caught separately because it is used to signal
582 # invalid keys on DynamicMap and should not warn
583 raise
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/util/__init__.py:1025, in Dynamic._dynamic_operation.<locals>.dynamic_operation(*key, **kwargs)
1023 def dynamic_operation(*key, **kwargs):
1024 key, obj = resolve(key, kwargs)
-> 1025 return apply(obj, *key, **kwargs)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/util/__init__.py:1017, in Dynamic._dynamic_operation.<locals>.apply(element, *key, **kwargs)
1015 def apply(element, *key, **kwargs):
1016 kwargs = dict(util.resolve_dependent_kwargs(self.p.kwargs), **kwargs)
-> 1017 processed = self._process(element, key, kwargs)
1018 if (self.p.link_dataset and isinstance(element, Dataset) and
1019 isinstance(processed, Dataset) and processed._dataset is None):
1020 processed._dataset = element.dataset
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/util/__init__.py:1001, in Dynamic._process(self, element, key, kwargs)
999 return self.p.operation.process_element(element, key, **kwargs)
1000 else:
-> 1001 return self.p.operation(element, **kwargs)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/hvplot/converter.py:1201, in HoloViewsConverter.__call__.<locals>.method_wrapper(ds, x, y)
1200 def method_wrapper(ds, x, y):
-> 1201 el = method(x, y, data=ds.data)
1202 el._transforms = dataset._transforms
1203 el._dataset = ds
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/hvplot/converter.py:2104, in HoloViewsConverter.image(self, x, y, z, data)
2102 element = self._get_element('image')
2103 if self.geo: params['crs'] = self.crs
-> 2104 return (element(data, [x, y], z, **params).redim(**redim)
2105 .apply(self._set_backends_opts, cur_opts=cur_opts, compat_opts=compat_opts))
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/element/raster.py:310, in Image.__init__(self, data, kdims, vdims, bounds, extents, xdensity, ydensity, rtol, **params)
308 xdensity = xdensity if xdensity else util.compute_density(l, r, dim1, self._time_unit)
309 ydensity = ydensity if ydensity else util.compute_density(b, t, dim2, self._time_unit)
--> 310 SheetCoordinateSystem.__init__(self, bounds, xdensity, ydensity)
311 if non_finite:
312 self.bounds = BoundingBox(points=((np.nan, np.nan), (np.nan, np.nan)))
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/sheetcoords.py:166, in SheetCoordinateSystem.__init__(self, bounds, xdensity, ydensity)
162 self.__set_ydensity(ydensity or xdensity)
164 self.lbrt = np.array(bounds.lbrt())
--> 166 r1,r2,c1,c2 = Slice._boundsspec2slicespec(self.lbrt,self)
167 self.__shape = (r2-r1,c2-c1)
File /usr/share/miniconda3/envs/kerchunk-cookbook/lib/python3.10/site-packages/holoviews/core/sheetcoords.py:518, in Slice._boundsspec2slicespec(boundsspec, scs)
515 t_m,l_m = scs.sheet2matrix(l,t)
516 b_m,r_m = scs.sheet2matrix(r,b)
--> 518 l_idx = int(np.ceil(l_m-0.5))
519 t_idx = int(np.ceil(t_m-0.5))
520 r_idx = int(np.floor(r_m+0.5))
ValueError: cannot convert float NaN to integer
:DynamicMap [time]