Search code examples
pythonbar-chartbokehholoviews

Have dimension labels for 2 key dimensions in a Holoview Bars plot (KeyError: Dimension not found)


I am new to Holoviews and encountering an issue when trying to label a multi-key bar plot.

See my tries below.

My data

import holoviews as hv
import pandas as pd

df = pd.DataFrame(
    {
        'period': [1, 2, 3, 1, 2, 3],
        'origin': ['a', 'a', 'a', 'b', 'b', 'b'],
        'value': [3, 5, 5, 1, 2, 4],
    }
)
print(df)

   period origin  value
0       1      a      3
1       2      a      5
2       3      a      5
3       1      b      1
4       2      b      2
5       3      b      4

Direct plotting

Plotting directly without creating hv.Dimension objects work:

hv.Bars(df, ['origin', 'period'], 'value')

yields the following (see how axes are labelled with dimension codes):

Grouped barplot

Stacked barplot with labelled Dimensions

Creating dimensions with label, as long as the second label does not have to appear, works:

origin_dim = hv.Dimension('origin', label='Origine')
period_dim = hv.Dimension('period', label='Période')
value_dim = hv.Dimension('value', label='Valeur')
hv.Bars(df, [origin_dim, period_dim], value_dim).opts(stacked=True)

gives me:

Stacked barplot

Only labelling first dimension

origin_dim = hv.Dimension('origin', label='Origine')
period_dim = hv.Dimension('period')
value_dim = hv.Dimension('value', label='Valeur')
hv.Bars(df, [origin_dim, period_dim], value_dim)

gives (second dimension is not labelled in this example):

singly labelled grouped barplot

Trying to label both key dimensions (does not work)

When trying to label both key dimensions, I get a KeyError exception.

origin_dim = hv.Dimension('origin', label='Origine')
period_dim = hv.Dimension('period', label='Période')
value_dim = hv.Dimension('value', label='Valeur')
hv.Bars(df, [origin_dim, period_dim], value_dim)

raises:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/IPython/core/formatters.py in __call__(self, obj, include, exclude)
    968 
    969             if method is not None:
--> 970                 return method(include=include, exclude=exclude)
    971             return None
    972         else:

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/core/dimension.py in _repr_mimebundle_(self, include, exclude)
   1310         combined and returned.
   1311         """
-> 1312         return Store.render(self)
   1313 
   1314 

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/core/options.py in render(cls, obj)
   1392         data, metadata = {}, {}
   1393         for hook in hooks:
-> 1394             ret = hook(obj)
   1395             if ret is None:
   1396                 continue

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/ipython/display_hooks.py in pprint_display(obj)
    280     if not ip.display_formatter.formatters['text/plain'].pprint:
    281         return None
--> 282     return display(obj, raw_output=True)
    283 
    284 

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/ipython/display_hooks.py in display(obj, raw_output, **kwargs)
    250     elif isinstance(obj, (CompositeOverlay, ViewableElement)):
    251         with option_state(obj):
--> 252             output = element_display(obj)
    253     elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
    254         with option_state(obj):

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/ipython/display_hooks.py in wrapped(element)
    144         try:
    145             max_frames = OutputSettings.options['max_frames']
--> 146             mimebundle = fn(element, max_frames=max_frames)
    147             if mimebundle is None:
    148                 return {}, {}

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/ipython/display_hooks.py in element_display(element, max_frames)
    190         return None
    191 
--> 192     return render(element)
    193 
    194 

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/ipython/display_hooks.py in render(obj, **kwargs)
     66         renderer = renderer.instance(fig='png')
     67 
---> 68     return renderer.components(obj, **kwargs)
     69 
     70 

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/plotting/renderer.py in components(self, obj, fmt, comm, **kwargs)
    393                 doc = Document()
    394                 with config.set(embed=embed):
--> 395                     model = plot.layout._render_model(doc, comm)
    396                 if embed:
    397                     return render_model(model, comm)

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/panel/viewable.py in _render_model(self, doc, comm)
    415         if comm is None:
    416             comm = state._comm_manager.get_server_comm()
--> 417         model = self.get_root(doc, comm)
    418 
    419         if config.embed:

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/panel/viewable.py in get_root(self, doc, comm)
    640         """
    641         doc = doc or _curdoc()
--> 642         root = self._get_model(doc, comm=comm)
    643         self._preprocess(root)
    644         ref = root.ref['id']

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/panel/layout.py in _get_model(self, doc, root, parent, comm)
    118         if root is None:
    119             root = model
--> 120         objects = self._get_objects(model, [], doc, root, comm)
    121         props = dict(self._init_properties(), objects=objects)
    122         model.update(**self._process_param_change(props))

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/panel/layout.py in _get_objects(self, model, old_objects, doc, root, comm)
    108             else:
    109                 try:
--> 110                     child = pane._get_model(doc, root, model, comm)
    111                 except RerenderError:
    112                     return self._get_objects(model, current_objects[:i], doc, root, comm)

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/panel/pane/holoviews.py in _get_model(self, doc, root, parent, comm)
    225             plot = self.object
    226         else:
--> 227             plot = self._render(doc, comm, root)
    228 
    229         plot.pane = self

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/panel/pane/holoviews.py in _render(self, doc, comm, root)
    284             kwargs = {}
    285 
--> 286         return renderer.get_plot(self.object, **kwargs)
    287 
    288     def _cleanup(self, root):

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/plotting/bokeh/renderer.py in get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     71         combining the bokeh model with another plot.
     72         """
---> 73         plot = super(BokehRenderer, self_or_cls).get_plot(obj, doc, renderer, **kwargs)
     74         if plot.document is None:
     75             plot.document = Document() if self_or_cls.notebook_context else curdoc()

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/plotting/renderer.py in get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    238             init_key = tuple(v if d is None else d for v, d in
    239                              zip(plot.keys[0], defaults))
--> 240             plot.update(init_key)
    241         else:
    242             plot = obj

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/plotting/plot.py in update(self, key)
    924     def update(self, key):
    925         if len(self) == 1 and ((key == 0) or (key == self.keys[0])) and not self.drawn:
--> 926             return self.initialize_plot()
    927         item = self.__getitem__(key)
    928         self.traverse(lambda x: setattr(x, '_updated', True))

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/plotting/bokeh/element.py in initialize_plot(self, ranges, plot, plots, source)
   1374         self.handles['plot'] = plot
   1375 
-> 1376         self._init_glyphs(plot, element, ranges, source)
   1377         if not self.overlaid:
   1378             self._update_plot(key, plot, style_element)

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/plotting/bokeh/element.py in _init_glyphs(self, plot, element, ranges, source)
   1318         else:
   1319             style = self.style[self.cyclic_index]
-> 1320             data, mapping, style = self.get_data(element, ranges, style)
   1321             current_id = element._plot_id
   1322 

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/plotting/bokeh/chart.py in get_data(self, element, ranges, style)
   1002                 if group_dim not in ds.dimensions():
   1003                     ds = ds.add_dimension(group_dim.name, ds.ndims, gval)
-> 1004                 data[group_dim.name].append(ds.dimension_values(group_dim))
   1005             else:
   1006                 data[xdim.name].append(ds.dimension_values(xdim))

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/core/data/__init__.py in pipelined_fn(*args, **kwargs)
    214 
    215             try:
--> 216                 result = method_fn(*args, **kwargs)
    217 
    218                 op = method_op.instance(

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/core/data/__init__.py in dimension_values(self, dimension, expanded, flat)
   1068             NumPy array of values along the requested dimension
   1069         """
-> 1070         dim = self.get_dimension(dimension, strict=True)
   1071         return self.interface.values(self, dim, expanded, flat)
   1072 

/var/lib/anaconda3/envs/py38/lib/python3.8/site-packages/holoviews/core/dimension.py in get_dimension(self, dimension, default, strict)
    970             dims = [d for d in all_dims if dimension == d]
    971             if strict and not dims:
--> 972                 raise KeyError("%r not found." % dimension)
    973             elif dims:
    974                 return dims[0]

KeyError: "Dimension('period', label='Période') not found."

:Bars   [origin,period]   (value)

Is using hv.Dimension labels the correct way to have "pretty" axis labels? Why am I getting a KeyError exception when trying to print my pretty label? How should I proceed to have my plot rendered as a pretty-labelled grouped bar plot?


Solution

  • Thanks to the links provided by @Sander, I was able to find an issue in the Holoviews github repository.

    It looks like this is a known bug: https://github.com/holoviz/holoviews/issues/4496