holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.66k stars 396 forks source link

The pointerXY stream breaks on heatmaps with categorical kdims #6232

Open blooop opened 1 month ago

blooop commented 1 month ago

ALL software version info

python 3.10 bokeh 3.4.1 holoviews 1.18.3 panel 1.4.2 ubuntu 22.04 chrome 124.0 notebook: N/A running as a python script with panel

Description of expected behavior and the observed behavior

Moving the mouse from inside to outside the heatmap with categorical axes should not trigger an exception.

I set up a PointerXY stream on a heatmap so I can run a callback on the value the pointer is hovering over. If I have a heatmap with integer or float axes, everything works as expected. If one of my axes has categorical values and I move across the top edge of the heatmap I get an IndexError exception for holoviews.

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

import pandas as pd
import holoviews as hv
import panel as pn

hv.extension("bokeh")

# create 2x2 dataset with integer keys for key1 and key2
df_integer = pd.DataFrame(
    ([[0, 0, 10], [0, 1, 20], [1, 0, 20], [1, 1, 30]]), columns=["key1", "key2", "value"]
)

# create 2x2 dataset with an integer key for key1 and a string key for key2
df_categorical = pd.DataFrame(
    ([[0, "A", 10], [0, "B", 20], [1, "A", 20], [1, "B", 30]]), columns=["key1", "key2", "value"]
)

def move_pointer(x, y):
    print(x, y)

def create_heatmap_streamxy(df: pd.DataFrame):
    """Take a dataframe and return a heatmap that has a streamxy callback."""
    ds = hv.Dataset(df)

    heatmap = ds.to(hv.HeatMap, kdims=["key1", "key2"], vdims="value").opts(tools=["hover"])
    hv.streams.PointerXY(source=heatmap).add_subscriber(move_pointer)
    return heatmap

row = pn.Row()
row.append(create_heatmap_streamxy(df_integer))
row.append(create_heatmap_streamxy(df_categorical))
row.show()

Stack traceback and/or browser JavaScript console output

Task exception was never retrieved
future: <Task finished name='Task-795' coro=<Callback.process_on_event() done, defined at /usr/local/lib/python3.10/site-packages/holoviews/plotting/bokeh/callbacks.py:328> exception=IndexError('list index out of range')>
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/holoviews/plotting/bokeh/callbacks.py", line 351, in process_on_event
    self.on_msg(msg)
  File "/usr/local/lib/python3.10/site-packages/holoviews/plotting/bokeh/callbacks.py", line 203, in on_msg
    processed_msg = self._process_msg(filtered_msg)
  File "/usr/local/lib/python3.10/site-packages/holoviews/plotting/bokeh/callbacks.py", line 503, in _process_msg
    msg['y'] = y_range.factors[int(msg['y'])]
IndexError: list index out of range