Failure to overlay text on bar chart.
ALL software version info
Python 3.10.6 hvplot==0.8.3 holoviews==1.15.4 bokeh==2.4.3 jupyterlab==3.6.3
Description of expected behavior and the observed behavior
Should be able to add text to a bar chart as described here: https://stackoverflow.com/questions/46223037/add-text-annotations-to-each-data-point-in-holoviews-plot
Complete, minimal, self-contained example code that reproduces the issue
import pandas as pd
import hvplot.pandas
import holoviews as hv
df = pd.DataFrame([[1,2,3,4,5]])
df.hvplot.bar() * hv.HLine(df[0].mean()) * hv.Text(1, df[0].mean(), "Mean")
--------------------------------------------------------------------------
ValueError Traceback (most recent call last)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/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 ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/core/dimension.py:1290, in Dimensioned._repr_mimebundle_(self, include, exclude)
1283 def _repr_mimebundle_(self, include=None, exclude=None):
1284 """
1285 Resolves the class hierarchy for the class rendering the
1286 object using any display hooks registered on Store.display
1287 hooks. The output of all registered display_hooks is then
1288 combined and returned.
1289 """
-> 1290 return Store.render(self)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/core/options.py:1425, in Store.render(cls, obj)
1423 data, metadata = {}, {}
1424 for hook in hooks:
-> 1425 ret = hook(obj)
1426 if ret is None:
1427 continue
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:279, in pprint_display(obj)
277 if not ip.display_formatter.formatters['text/plain'].pprint:
278 return None
--> 279 return display(obj, raw_output=True)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:247, in display(obj, raw_output, **kwargs)
245 elif isinstance(obj, (CompositeOverlay, ViewableElement)):
246 with option_state(obj):
--> 247 output = element_display(obj)
248 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
249 with option_state(obj):
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:141, in display_hook.<locals>.wrapped(element)
139 try:
140 max_frames = OutputSettings.options['max_frames']
--> 141 mimebundle = fn(element, max_frames=max_frames)
142 if mimebundle is None:
143 return {}, {}
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:187, in element_display(element, max_frames)
184 if type(element) not in Store.registry[backend]:
185 return None
--> 187 return render(element)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:68, in render(obj, **kwargs)
65 if renderer.fig == 'pdf':
66 renderer = renderer.instance(fig='png')
---> 68 return renderer.components(obj, **kwargs)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/plotting/renderer.py:394, in Renderer.components(self, obj, fmt, comm, **kwargs)
391 embed = (not (dynamic or streams or self.widget_mode == 'live') or config.embed)
393 if embed or config.comms == 'default':
--> 394 return self._render_panel(plot, embed, comm)
395 return self._render_ipywidget(plot)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/plotting/renderer.py:401, in Renderer._render_panel(self, plot, embed, comm)
399 doc = Document()
400 with config.set(embed=embed):
--> 401 model = plot.layout._render_model(doc, comm)
402 if embed:
403 return render_model(model, comm)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/panel/viewable.py:509, in Renderable._render_model(self, doc, comm)
507 if comm is None:
508 comm = state._comm_manager.get_server_comm()
--> 509 model = self.get_root(doc, comm)
511 if config.embed:
512 embed_state(self, model, doc,
513 json=config.embed_json,
514 json_prefix=config.embed_json_prefix,
515 save_path=config.embed_save_path,
516 load_path=config.embed_load_path,
517 progress=False)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/panel/viewable.py:560, in Renderable.get_root(self, doc, comm, preprocess)
543 """
544 Returns the root model and applies pre-processing hooks
545
(...)
557 Returns the bokeh model corresponding to this panel object
558 """
559 doc = init_doc(doc)
--> 560 root = self._get_model(doc, comm=comm)
561 if preprocess:
562 self._preprocess(root)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/panel/layout/base.py:146, in Panel._get_model(self, doc, root, parent, comm)
144 if root is None:
145 root = model
--> 146 objects = self._get_objects(model, [], doc, root, comm)
147 props = dict(self._init_params(), objects=objects)
148 model.update(**self._process_param_change(props))
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/panel/layout/base.py:131, in Panel._get_objects(self, model, old_objects, doc, root, comm)
129 else:
130 try:
--> 131 child = pane._get_model(doc, root, model, comm)
132 except RerenderError:
133 return self._get_objects(model, current_objects[:i], doc, root, comm)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/panel/pane/holoviews.py:367, in HoloViews._get_model(self, doc, root, parent, comm)
365 plot = self.object
366 else:
--> 367 plot = self._render(doc, comm, root)
369 plot.pane = self
370 backend = plot.renderer.backend
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/panel/pane/holoviews.py:442, in HoloViews._render(self, doc, comm, root)
439 if comm:
440 kwargs['comm'] = comm
--> 442 return renderer.get_plot(self.object, **kwargs)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/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 ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/plotting/renderer.py:236, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
233 defaults = [kd.default for kd in plot.dimensions]
234 init_key = tuple(v if d is None else d for v, d in
235 zip(plot.keys[0], defaults))
--> 236 plot.update(init_key)
237 else:
238 plot = obj
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/plotting/plot.py:936, in DimensionedPlot.update(self, key)
934 def update(self, key):
935 if len(self) == 1 and ((key == 0) or (key == self.keys[0])) and not self.drawn:
--> 936 return self.initialize_plot()
937 item = self.__getitem__(key)
938 self.traverse(lambda x: setattr(x, '_updated', True))
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/plotting/bokeh/element.py:2327, in OverlayPlot.initialize_plot(self, ranges, plot, plots)
2325 if plot and not self.overlaid:
2326 self._update_plot(key, plot, element)
-> 2327 self._update_ranges(element, ranges)
2329 panels = []
2330 for key, subplot in self.subplots.items():
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/plotting/bokeh/element.py:898, in ElementPlot._update_ranges(self, element, ranges)
895 xupdate, yupdate = False, False
897 if not self.drawn or xupdate:
--> 898 self._update_range(x_range, l, r, xfactors, self.invert_xaxis,
899 self._shared['x'], self.logx, streaming)
900 if not self.drawn or yupdate:
901 self._update_range(y_range, b, t, yfactors, self.invert_yaxis,
902 self._shared['y'], self.logy, streaming)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/holoviews/plotting/bokeh/element.py:945, in ElementPlot._update_range(self, axis_range, low, high, factors, invert, shared, log, streaming)
943 factors = list(decode_bytes(factors))
944 if invert: factors = factors[::-1]
--> 945 axis_range.factors = factors
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/bokeh/core/has_props.py:230, in HasProps.__setattr__(self, name, value)
228 properties = self.properties(_with_props=True)
229 if name in properties:
--> 230 return super().__setattr__(name, value)
232 descriptor = getattr(self.__class__, name, None)
233 if isinstance(descriptor, property): # Python property
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/bokeh/core/property/descriptors.py:283, in PropertyDescriptor.__set__(self, obj, value, setter)
280 class_name = obj.__class__.__name__
281 raise RuntimeError(f"{class_name}.{self.name} is a readonly property")
--> 283 value = self.property.prepare_value(obj, self.name, value)
284 old = self._get(obj)
285 self._set(obj, old, value, setter=setter)
File ~/.cache/pypoetry/virtualenvs/stakeholderstudy-shsA1FjX-py3.10/lib/python3.10/site-packages/bokeh/core/property/bases.py:365, in Property.prepare_value(self, owner, name, value, hint)
363 else:
364 obj_repr = owner if isinstance(owner, HasProps) else owner.__name__
--> 365 raise ValueError(f"failed to validate {obj_repr}.{name}: {error}")
367 if isinstance(owner, HasProps):
368 obj = owner
ValueError: failed to validate FactorRange(id='1159', ...).factors: expected an element of either Seq(String), Seq(Tuple(String, String)) or Seq(Tuple(String, String, String)), got [('0', '0'), ('0', '1'), ('0', '2'), ('0', '3'), ('0', '4'), '1']
I can reproduce it with the code given above.
Only plotting the bar returns this:

Where all bars are plotted over each other. Transposing the DataFrame gives a more desirable look:

Which is able to be used with hv.Text:

I'm getting a similar problem in a different situation when trying to compose two bar plots using + operator.
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/panel/viewable.py:591, in Renderable._preprocess(self, root, changed, old_models)
590 try:
--> 591 hook(self, root, changed, old_models)
592 except TypeError:
TypeError: link_axes() takes 2 positional arguments but 4 were given
During handling of the above exception, another exception occurred:
ValueError Traceback (most recent call last)
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/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 ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/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 ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/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 ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/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 ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:251, in display(obj, raw_output, **kwargs)
249 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
250 with option_state(obj):
--> 251 output = layout_display(obj)
252 elif isinstance(obj, (HoloMap, DynamicMap)):
253 with option_state(obj):
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/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 ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/holoviews/ipython/display_hooks.py:216, in layout_display(layout, max_frames)
213 max_frame_warning(max_frames)
214 return None
--> 216 return render(layout)
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/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 ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/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 ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/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 ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/panel/viewable.py:736, in Viewable._render_model(self, doc, comm)
734 if comm is None:
735 comm = state._comm_manager.get_server_comm()
--> 736 model = self.get_root(doc, comm)
738 if self._design and self._design.theme.bokeh_theme:
739 doc.theme = self._design.theme.bokeh_theme
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/panel/layout/base.py:305, in Panel.get_root(self, doc, comm, preprocess)
301 def get_root(
302 self, doc: Optional[Document] = None, comm: Optional[Comm] = None,
303 preprocess: bool = True
304 ) -> Model:
--> 305 root = super().get_root(doc, comm, preprocess)
306 # ALERT: Find a better way to handle this
307 if hasattr(root, 'styles') and 'overflow-x' in root.styles:
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/panel/viewable.py:660, in Renderable.get_root(self, doc, comm, preprocess)
658 root = self._get_model(doc, comm=comm)
659 if preprocess:
--> 660 self._preprocess(root)
661 else:
662 root = wrapper.get_root(doc, comm, preprocess)
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/panel/viewable.py:593, in Renderable._preprocess(self, root, changed, old_models)
591 hook(self, root, changed, old_models)
592 except TypeError:
--> 593 hook(self, root)
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/panel/pane/holoviews.py:872, in link_axes(root_view, root_model)
870 if tag in fig.x_range.tags and axis is not fig.x_range:
871 if hasattr(axis, 'factors'):
--> 872 axis.factors = list(unique_iterator(axis.factors+fig.x_range.factors))
873 fig.x_range = axis
874 p.handles['x_range'] = axis
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/bokeh/core/has_props.py:327, in HasProps.__setattr__(self, name, value)
325 properties = self.properties(_with_props=True)
326 if name in properties:
--> 327 return super().__setattr__(name, value)
329 descriptor = getattr(self.__class__, name, None)
330 if isinstance(descriptor, property): # Python property
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/bokeh/core/property/descriptors.py:332, in PropertyDescriptor.__set__(self, obj, value, setter)
329 class_name = obj.__class__.__name__
330 raise RuntimeError(f"{class_name}.{self.name} is a readonly property")
--> 332 value = self.property.prepare_value(obj, self.name, value)
333 old = self._get(obj)
334 self._set(obj, old, value, setter=setter)
File ~/.cache/pypoetry/virtualenvs/alloha-S7RXN1yG-py3.10/lib/python3.10/site-packages/bokeh/core/property/bases.py:364, in Property.prepare_value(self, owner, name, value, hint)
362 else:
363 obj_repr = owner if isinstance(owner, HasProps) else owner.__name__
--> 364 raise ValueError(f"failed to validate {obj_repr}.{name}: {error}")
366 if isinstance(owner, HasProps):
367 obj = owner
ValueError: failed to validate FactorRange(id='p14208', ...).factors: expected an element of either Seq(String), Seq(Tuple(String, String)) or Seq(Tuple(String, String, String)), got [('0xe6424ab2', 'QF Matched + SME'), ('0xe6424ab2', 'QF Matched'), ('0xe6424ab2', 'QF Unmatched'), ('0x4cd41869', 'QF Matched + SME'), ('0x4cd41869', 'QF Matched'), ('0x4cd41869', 'QF Unmatched'), ('0xdd9b885d', 'QF Matched + SME'), ('0xdd9b885d', 'QF Matched'), ('0xdd9b885d', 'QF Unmatched'), ('0x8d6f0c7b', 'QF Matched + SME'), ('0x8d6f0c7b', 'QF Matched'), ('0x8d6f0c7b', 'QF Unmatched'), ('0xa9bdf738', 'QF Matched + SME'), ('0xa9bdf738', 'QF Matched'), ('0xa9bdf738', 'QF Unmatched'), ('0xe8249a10', 'QF Matched + SME'), ('0xe8249a10', 'QF Matched'), ('0xe8249a10', 'QF Unmatched'), ('0x64a30a4b', 'QF Matched + SME'), ('0x64a30a4b', 'QF Matched'), ('0x64a30a4b', 'QF Unmatched'), ('0x97589cd1', 'QF Matched + SME'), ('0x97589cd1', 'QF Matched'), ('0x97589cd1', 'QF Unmatched'), ('0x23387567', 'QF Matched + SME'), ('0x23387567', 'QF Matched'), ('0x23387567', 'QF Unmatched'), ('0xec026845', 'QF Matched + SME'), ('0xec026845', 'QF Matched'), ('0xec026845', 'QF Unmatched'), ('0xf1f4942d', 'QF Matched + SME'), ('0xf1f4942d', 'QF Matched'), ('0xf1f4942d', 'QF Unmatched'), ('0x72b0d6a6', 'QF Matched + SME'), ('0x72b0d6a6', 'QF Matched'), ('0x72b0d6a6', 'QF Unmatched'), ('0x5351510d', 'QF Matched + SME'), ('0x5351510d', 'QF Matched'), ('0x5351510d', 'QF Unmatched'), ('0x10b3f00e', 'QF Matched + SME'), ('0x10b3f00e', 'QF Matched'), ('0x10b3f00e', 'QF Unmatched'), ('0xc401c980', 'QF Matched + SME'), ('0xc401c980', 'QF Matched'), ('0xc401c980', 'QF Unmatched'), ('0xcf3165f4', 'QF Matched + SME'), ('0xcf3165f4', 'QF Matched'), ('0xcf3165f4', 'QF Unmatched'), '0xe6424ab2', '0x4cd41869', '0xdd9b885d', '0x8d6f0c7b', '0xa9bdf738', '0xe8249a10', '0x64a30a4b', '0x97589cd1', '0x23387567', '0xec026845', '0xf1f4942d', '0x72b0d6a6', '0x5351510d', '0x10b3f00e', '0xc401c980', '0xcf3165f4']
:Layout
.Bars.I :Bars [projectId,Variable] (value)
.Overlay.I :Overlay
.Bars.I :Bars [projectId] (Percentage Boost)
.HLine.I :HLine [x,y]
```
Can you share a minimal, reproducible example (MRE)?
Can you share a minimal, reproducible example (MRE)?
Yep!
Here you go:
import pandas as pd
import hvplot.pandas
import holoviews as hv
df = pd.DataFrame([[1,2,3,4,5],[1,2,3,4,5]])
df.hvplot.bar(y=['0','1']).opts(multi_level=False) + df.hvplot.bar(y='0')
Looking at the error, I can see it happens in Panel, so the fix maybe be needed there. As a work-around for now you can
import pandas as pd
import hvplot.pandas
import holoviews as hv
df = pd.DataFrame([[1,2,3,4,5],[1,2,3,4,5]])
(df.hvplot.bar(y=['0','1']).opts(multi_level=False) + df.hvplot.bar(y='0')).opts(shared_axes=False)