holoviz / holoviz/hvplot

Unable to group xarray.DataArray by date coordinate in hvplot.line()

Open
#1,582 1 comment 0 reactions 0 assignees View on GitHub
upstream
Dominant language
Python
Stars
1.4k
Forks
124
Avg merge
1d 18h
Merged PRs (30d)
1

Description

#### ALL software version info

Software Version Info

```plaintext
hvplot=0.11.3
holoviews=1.20.2
xarray=2025.4.0
```

#### Description of expected behavior and the observed behavior
As described [here](https://discourse.holoviz.org/t/groupby-date-shows-empty-plot/5688) I am trying to group my timeseries data by dates from an xarray.DataArray using da.hvplot.line().
In previous versions that worked fine (hvplot=0.10.0, holoviews=1.19.1, xarray=2022.9.0), now I get an `IndexError: Boolean index has wrong length:...`

#### Complete, minimal, self-contained example code that reproduces the issue
Does not work in python (using panel), nor in jupyter
```python
import numpy as np
import xarray as xr
import hvplot.xarray
import pandas as pd
import holoviews as hv
hv.extension('bokeh')
time = pd.date_range("2023-01-01", periods=48, freq="1h")
sample = np.arange(3)

# Create dummy 3D data
data = np.random.rand(len(time), len(sample))
da = xr.DataArray(data, dims=("time", "sample"), coords={
"time": time,
"sample": sample,
})

da = da.assign_coords(date=("time", da.time.dt.strftime('%Y-%m-%d').data))

# This works
# da.hvplot.line(x="time", groupby=["date"], grid=True)
# This works too
# da.hvplot.line(x="time", groupby=["sample"], grid=True)
# This works not
da.hvplot.line(x="time", groupby=["date", "sample"], grid=True)

```

#### Stack traceback and/or browser JavaScript console output
```
WARNING:param.load_subset: Callable raised "IndexError('Boolean index has wrong length: 144 instead of 48')".
Invoked as load_subset(np.str_('2023-01-01'), np.int64(0))
WARNING:param.dynamic_operation: Callable raised "IndexError('Boolean index has wrong length: 144 instead of 48')".
Invoked as dynamic_operation(np.str_('2023-01-01'), np.int64(0))

---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/IPython/core/formatters.py:1036, in MimeBundleFormatter.__call__(self, obj, include, exclude)
1033 method = get_real_method(obj, self.print_method)
1035 if method is not None:
-> 1036 return method(include=include, exclude=exclude)
1037 return None
1038 else:

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/dimension.py:1277, in Dimensioned._repr_mimebundle_(self, include, exclude)
1270 def _repr_mimebundle_(self, include=None, exclude=None):
1271 """
1272 Resolves the class hierarchy for the class rendering the
1273 object using any display hooks registered on Store.display
1274 hooks. The output of all registered display_hooks is then
1275 combined and returned.
1276 """
-> 1277 return Store.render(self)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/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 /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:287, in pprint_display(obj)
285 if not ip.display_formatter.formatters['text/plain'].pprint:
286 return None
--> 287 return display(obj, raw_output=True)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:261, in display(obj, raw_output, **kwargs)
259 elif isinstance(obj, (HoloMap, DynamicMap)):
260 with option_state(obj):
--> 261 output = map_display(obj)
262 elif isinstance(obj, Plot):
263 output = render(obj)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:149, in display_hook..wrapped(element)
147 try:
148 max_frames = OutputSettings.options['max_frames']
--> 149 mimebundle = fn(element, max_frames=max_frames)
150 if mimebundle is None:
151 return {}, {}

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:209, in map_display(vmap, max_frames)
206 max_frame_warning(max_frames)
207 return None
--> 209 return render(vmap)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:76, in render(obj, **kwargs)
73 if renderer.fig == 'pdf':
74 renderer = renderer.instance(fig='png')
---> 76 return renderer.components(obj, **kwargs)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/plotting/renderer.py:396, in Renderer.components(self, obj, fmt, comm, **kwargs)
394 embed = (not (dynamic or streams or self.widget_mode == 'live') or config.embed)
395 if embed or config.comms == 'default':
--> 396 return self._render_panel(plot, embed, comm)
397 return self._render_ipywidget(plot)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/plotting/renderer.py:403, in Renderer._render_panel(self, plot, embed, comm)
401 doc = Document()
402 with config.set(embed=embed):
--> 403 model = plot.layout._render_model(doc, comm)
404 if embed:
405 return render_model(model, comm)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/panel/viewable.py:768, in Viewable._render_model(self, doc, comm)
766 if comm is None:
767 comm = state._comm_manager.get_server_comm()
--> 768 model = self.get_root(doc, comm)
770 if self._design and self._design.theme.bokeh_theme:
771 doc.theme = self._design.theme.bokeh_theme

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/panel/layout/base.py:329, in Panel.get_root(self, doc, comm, preprocess)
325 def get_root(
326 self, doc: Document | None = None, comm: Comm | None = None,
327 preprocess: bool = True
328 ) -> Model:
--> 329 root = super().get_root(doc, comm, preprocess)
330 # ALERT: Find a better way to handle this
331 if hasattr(root, 'styles') and 'overflow-x' in root.styles:

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/panel/viewable.py:698, in Renderable.get_root(self, doc, comm, preprocess)
696 wrapper = self._design._wrapper(self)
697 if wrapper is self:
--> 698 root = self._get_model(doc, comm=comm)
699 if preprocess:
700 self._preprocess(root)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/panel/layout/base.py:313, in Panel._get_model(self, doc, root, parent, comm)
311 root = root or model
312 self._models[root.ref['id']] = (model, parent)
--> 313 objects, _ = self._get_objects(model, [], doc, root, comm)
314 props = self._get_properties(doc)
315 props[self._property_mapping['objects']] = objects

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/panel/layout/base.py:295, in Panel._get_objects(self, model, old_objects, doc, root, comm)
293 else:
294 try:
--> 295 child = pane._get_model(doc, root, model, comm)
296 except RerenderError as e:
297 if e.layout is not None and e.layout is not self:

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/panel/pane/holoviews.py:437, in HoloViews._get_model(self, doc, root, parent, comm)
435 plot = self.object
436 else:
--> 437 plot = self._render(doc, comm, root)
439 plot.pane = self
440 backend = plot.renderer.backend

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/panel/pane/holoviews.py:531, in HoloViews._render(self, doc, comm, root)
528 if comm:
529 kwargs['comm'] = comm
--> 531 return renderer.get_plot(self.object, **kwargs)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/plotting/bokeh/renderer.py:68, in BokehRenderer.get_plot(self_or_cls, obj, doc, renderer, **kwargs)
61 @bothmethod
62 def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs):
63 """
64 Given a HoloViews Viewable return a corresponding plot instance.
65 Allows supplying a document attach the plot to, useful when
66 combining the bokeh model with another plot.
67 """
---> 68 plot = super().get_plot(obj, doc, renderer, **kwargs)
69 if plot.document is None:
70 plot.document = Document() if self_or_cls.notebook_context else curdoc()

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/plotting/renderer.py:216, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
213 raise SkipRendering(msg.format(dims=dims))
215 # Initialize DynamicMaps with first data item
--> 216 initialize_dynamic(obj)
218 if not renderer:
219 renderer = self_or_cls

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/plotting/util.py:277, in initialize_dynamic(obj)
275 continue
276 if not len(dmap):
--> 277 dmap[dmap._initial_key()]

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/spaces.py:1216, in DynamicMap.__getitem__(self, key)
1214 # Not a cross product and nothing cached so compute element.
1215 if cache is not None: return cache
-> 1216 val = self._execute_callback(*tuple_key)
1217 if data_slice:
1218 val = self._dataslice(val, data_slice)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/spaces.py:983, in DynamicMap._execute_callback(self, *args)
980 kwargs['_memoization_hash_'] = hash_items
982 with dynamicmap_memoization(self.callback, self.streams):
--> 983 retval = self.callback(*args, **kwargs)
984 return self._style(retval)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/spaces.py:581, in Callable.__call__(self, *args, **kwargs)
578 args, kwargs = (), dict(pos_kwargs, **kwargs)
580 try:
--> 581 ret = self.callable(*args, **kwargs)
582 except KeyError:
583 # KeyError is caught separately because it is used to signal
584 # invalid keys on DynamicMap and should not warn
585 raise

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/util/__init__.py:1052, in Dynamic._dynamic_operation..dynamic_operation(*key, **kwargs)
1051 def dynamic_operation(*key, **kwargs):
-> 1052 key, obj = resolve(key, kwargs)
1053 return apply(obj, *key, **kwargs)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/util/__init__.py:1041, in Dynamic._dynamic_operation..resolve(key, kwargs)
1039 elif isinstance(map_obj, DynamicMap) and map_obj._posarg_keys and not key:
1040 key = tuple(kwargs[k] for k in map_obj._posarg_keys)
-> 1041 return key, map_obj[key]

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/spaces.py:1216, in DynamicMap.__getitem__(self, key)
1214 # Not a cross product and nothing cached so compute element.
1215 if cache is not None: return cache
-> 1216 val = self._execute_callback(*tuple_key)
1217 if data_slice:
1218 val = self._dataslice(val, data_slice)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/spaces.py:983, in DynamicMap._execute_callback(self, *args)
980 kwargs['_memoization_hash_'] = hash_items
982 with dynamicmap_memoization(self.callback, self.streams):
--> 983 retval = self.callback(*args, **kwargs)
984 return self._style(retval)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/spaces.py:581, in Callable.__call__(self, *args, **kwargs)
578 args, kwargs = (), dict(pos_kwargs, **kwargs)
580 try:
--> 581 ret = self.callable(*args, **kwargs)
582 except KeyError:
583 # KeyError is caught separately because it is used to signal
584 # invalid keys on DynamicMap and should not warn
585 raise

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/data/__init__.py:987, in Dataset.groupby..load_subset(*args)
985 def load_subset(*args):
986 constraint = dict(zip(dim_names, args))
--> 987 group = self.select(**constraint)
988 if np.isscalar(group):
989 return group_type(([group],), group=self.group,
990 label=self.label, vdims=self.vdims)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/data/__init__.py:195, in PipelineMeta.pipelined..pipelined_fn(*args, **kwargs)
192 inst._in_method = True
194 try:
--> 195 result = method_fn(*args, **kwargs)
196 if PipelineMeta.disable:
197 return result

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/data/__init__.py:622, in Dataset.select(self, selection_expr, selection_specs, **selection)
620 # Handle selection kwargs
621 if selection:
--> 622 data = self.interface.select(self, **selection)
623 else:
624 data = self.data

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/holoviews/core/data/pandas.py:418, in PandasInterface.select(cls, dataset, selection_mask, **selection)
416 df = df[selection_mask]
417 elif selection_mask is not None:
--> 418 df = df.iloc[selection_mask]
419 if indexed and len(df) == 1 and len(dataset.vdims) == 1:
420 return df[dataset.vdims[0].name].iloc[0]

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/pandas/core/indexing.py:1191, in _LocationIndexer.__getitem__(self, key)
1189 maybe_callable = com.apply_if_callable(key, self.obj)
1190 maybe_callable = self._check_deprecated_callable_usage(key, maybe_callable)
-> 1191 return self._getitem_axis(maybe_callable, axis=axis)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/pandas/core/indexing.py:1739, in _iLocIndexer._getitem_axis(self, key, axis)
1737 if com.is_bool_indexer(key):
1738 self._validate_key(key, axis)
-> 1739 return self._getbool_axis(key, axis=axis)
1741 # a list of integers
1742 elif is_list_like_indexer(key):

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/pandas/core/indexing.py:1209, in _LocationIndexer._getbool_axis(self, key, axis)
1205 @final
1206 def _getbool_axis(self, key, axis: AxisInt):
1207 # caller is responsible for ensuring non-None axis
1208 labels = self.obj._get_axis(axis)
-> 1209 key = check_bool_indexer(labels, key)
1210 inds = key.nonzero()[0]
1211 return self.obj._take_with_is_copy(inds, axis=axis)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/pandas/core/indexing.py:2681, in check_bool_indexer(index, key)
2677 elif not is_array_like(result):
2678 # GH 33924
2679 # key may contain nan elements, check_array_indexer needs bool array
2680 result = pd_array(result, dtype=bool)
-> 2681 return check_array_indexer(index, result)

File /localdata1/miniforge3/conda-envs/fabe_y1/irr_pred/lib/python3.10/site-packages/pandas/core/indexers/utils.py:539, in check_array_indexer(array, indexer)
537 # GH26658
538 if len(indexer) != len(array):
--> 539 raise IndexError(
540 f"Boolean index has wrong length: "
541 f"{len(indexer)} instead of {len(array)}"
542 )
543 elif is_integer_dtype(dtype):
544 try:

IndexError: Boolean index has wrong length: 144 instead of 48
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.