hvplot icon indicating copy to clipboard operation
hvplot copied to clipboard

Error when no data: bokeh backend could not plot any Elements in the Overlay

Open matipos2 opened this issue 10 months ago • 3 comments

Software Version Info
panel: 1.6.0
os: fedora
browser: chrome
Similar behavior on pandas and duckdb as a source.

Description of expected behavior and the observed behavior

I want to have a grid of subplots divided by year and month. I have also a checkbox group, so I can decide what data I want to see on the plot. For some of the values in checkbox there is no data for particular year / month. In that case there is an error.

The same behavior if I implement this by pn.GridBox and add each scatter plot separately to the grid with the loop.

Complete, minimal, self-contained example code that reproduces the issue

import pandas as pd
import hvplot.pandas
import panel as pn

my_data = pd.DataFrame(
    {
        'date': ['2023-05-08', '2023-05-18', '2023-06-21', '2023-07-01', '2024-05-01', '2024-06-01', '2024-06-03', '2024-07-06', '2024-07-21'],
        'type': ['A', 'B', 'A', 'A', 'A', 'B', 'A', 'A', 'B'],
        'value': [5, 4, 3, 2, 4, 8, 2, 2, 8]
    }
)
my_data['date'] = pd.to_datetime(my_data['date'])
my_data['date_year'] = my_data['date'].dt.year
my_data['date_month'] = my_data['date'].dt.month

pn.extension(theme='dark')

tickers_list = my_data['type'].unique().tolist()
ticker_group = pn.widgets.CheckBoxGroup(name='', value=['A',], options=tickers_list, inline=False, width=50)


def col_row_chart(data:pd.DataFrame, types):
    # too many charts kill the app, only last three years to show
    # this chart doesn't work if there are other charts on the same webpage
    data = data[data['type'].isin(types)]
    return data.hvplot.scatter(x='date', y='value', by='type', row='date_year', col='date_month', xaxis=False,
                               yaxis=True)


bound_dividends_tickers = pn.bind(col_row_chart, data=my_data, types=ticker_group)

dividends_layout = pn.Row(name='Dividends', sizing_mode='stretch_both')
dividends_layout.append(ticker_group)
dividends_layout.append(bound_dividends_tickers)

dividends_layout.servable()

Stack traceback and/or browser JavaScript console output

2025-02-17 20:58:54,110 ERROR: panel.reactive - Callback failed for object named '' changing property {'value': ['B']} 
Traceback (most recent call last):
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/panel/reactive.py", line 470, in _process_events
    self.param.update(**self_params)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/panel/param.py", line 897, in _replace_pane
    self._update_inner(new_object)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/panel/pane/base.py", line 743, in _update_inner
    new_pane, internal = self._update_from_object(
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/panel/pane/base.py", line 732, in _update_from_object
    old_object.object = object
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 515, in _f
    instance_param.__set__(obj, val)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 517, in _f
    return f(self, obj, val)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 1564, in __set__
    obj.param._call_watcher(watcher, event)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 2604, in _call_watcher
    self_._execute_watcher(watcher, (event,))
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/panel/pane/base.py", line 430, in _update_pane
    self._update_object(ref, doc, root, parent, comm)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/panel/pane/base.py", line 351, in _update_object
    new_model = self._get_model(doc, root, parent, comm)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/panel/pane/holoviews.py", line 437, in _get_model
    plot = self._render(doc, comm, root)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/panel/pane/holoviews.py", line 531, in _render
    return renderer.get_plot(self.object, **kwargs)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/holoviews/plotting/bokeh/renderer.py", line 68, in get_plot
    plot = super().get_plot(obj, doc, renderer, **kwargs)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/holoviews/plotting/renderer.py", line 234, in get_plot
    plot = self_or_cls.plotting_class(obj)(obj, renderer=renderer,
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/holoviews/plotting/bokeh/plot.py", line 505, in __init__
    self.subplots, self.layout = self._create_subplots(layout, ranges)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/holoviews/plotting/bokeh/plot.py", line 585, in _create_subplots
    subplot = plotting_class(view, dimensions=self.dimensions,
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/holoviews/plotting/bokeh/element.py", line 3075, in __init__
    super().__init__(overlay, **kwargs)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/holoviews/plotting/plot.py", line 1768, in __init__
    self.subplots = self._create_subplots(ranges)
  File "/matt/projekty/python/panel-intro/.venv/lib/python3.10/site-packages/holoviews/plotting/plot.py", line 1844, in _create_subplots
    raise SkipRendering(f"{self.renderer.backend} backend could not plot any Elements "
holoviews.core.options.SkipRendering: bokeh backend could not plot any Elements in the Overlay.

Screenshots or screencasts of the bug in action

As long as A is selected it works fine. Additional issue: take a look how this plot looks like on the web browser. date_year is not well positioned. If I use sizing_mode='stretch_width' it will look even worse.

Image

Then select A and B and unselect A: The error will occur (above).

Image

  • [ ] I may be interested in making a pull request to address this

matipos2 avatar Feb 17 '25 20:02 matipos2

This issue has been mentioned on HoloViz Discourse. There might be relevant details there:

https://discourse.holoviz.org/t/show-plot-when-there-is-no-data/8584/3

holovizbot avatar Feb 17 '25 20:02 holovizbot

This error seems to happen because the GridSpace contains empty NdLayouts:

import pandas as pd
import hvplot.pandas
import panel as pn
df = pd.DataFrame(
    {
        'date': ['2023-05-08', '2023-05-18', '2023-06-21', '2023-07-01', '2024-05-01', '2024-06-01', '2024-06-03', '2024-07-06', '2024-07-21'],
        'type': ['A', 'B', 'A', 'A', 'A', 'B', 'A', 'A', 'B'],
        'value': [5, 4, 3, 2, 4, 8, 2, 2, 8]
    }
)
df['date'] = pd.to_datetime(df['date'])
df['date_year'] = df['date'].dt.year
df['date_month'] = df['date'].dt.month

sdf = df[df['type'].isin(['B'])]

plot = sdf.hvplot.scatter(
    x='date', y='value', by='type', row='date_year', col='date_month',
    xaxis=False, yaxis=True,
)

Image

A workaround is to use color='type' instead of by='type', this avoids creating NdOverlays, by-passing the issue entirely.

Image

Setting dynamic=False also helps. Internally, hvPlot creates a DynamicMap as a result of a groupby operation with dynamic=True. It then calls .grid(<dims>) on this object. I tend to think that, when the output is meant to be a GridSpace, creating internally a DynamicMap instead of a HoloMap isn't very useful? cc @hoxbro

Image


Additional issue: take a look how this plot looks like on the web browser. date_year is not well positioned. If I use sizing_mode='stretch_width' it will look even worse.

This is a known issue tracked in HoloViews, see https://github.com/holoviz/holoviews/issues/5438.

maximlt avatar May 20 '25 13:05 maximlt

I tend to think that, when the output is meant to be a GridSpace, creating internally a DynamicMap instead of a HoloMap isn't very useful? cc @hoxbro

It doesn't seem useful to me either, but it is hard to say without looking at the code.

hoxbro avatar May 22 '25 17:05 hoxbro