How to set up HoverTool for multiple columns with hvPlot?

I was trying to add a hovering tooltip to each of the lines plotted with hvPlot.

The Dataframe looks like:

        Month-Day-3 regions 	1982-1983 	    2008-2009 	    2010-2011 	    2019-2020 	    2020-2021 	    2021-2022 	    2022-2023
365 	1900-07-01 	South West 	0.2263919000 	12.5141000000 	6.6907450000 	2.4357917000 	0.5901663300 	0.1001446200 	1.0919017000
366 	1900-07-02 	South West 	0.2514822800 	13.8615330000 	12.2373830000 	3.6159074000 	8.7875630000 	1.4974693000 	1.9742589000
367 	1900-07-03 	South West 	0.4882140200 	14.4603040000 	14.9851780000 	4.1348520000 	12.7190895000 	11.5023140000 	2.4423716000
368 	1900-07-04 	South West 	1.4404917000 	15.3638480000 	15.6692705000 	4.3111353000 	17.7306600000 	16.1637740000 	2.7225597000
369 	1900-07-05 	South West 	3.5807664000 	15.3754170000 	15.7712230000 	4.3848157000 	19.6496050000 	23.4813460000 	3.1032538000
370 	1900-07-06 	South West 	7.8110630000 	15.6621120000 	15.7997120000 	4.4555316000 	21.3921930000 	24.0971810000 	3.2181490000
371 	1900-07-07 	South West 	9.4692700000 	25.7376730000 	16.4730300000 	5.2979030000 	22.1507600000 	24.1890100000 	3.7347070000
372 	1900-07-08 	South West 	10.3100510000 	45.7029650000 	16.6521340000 	13.1908170000 	22.3775860000 	24.2017360000 	6.1997110000
373 	1900-07-09 	South West 	10.5075210000 	55.3129430000 	16.6757770000 	14.5884300000 	22.4045560000 	24.2094000000 	9.5680410000
   ... ...
727 	1901-06-28 	South West 	554.2284500000 	590.9046000000 	1,032.5052000000 	647.4787000000 	751.0833000000 	677.0237000000 	NaN
728 	1901-06-29 	South West 	558.6062000000 	591.6897600000 	1,032.5798000000 	647.6735000000 	751.1294600000 	677.0494400000 	NaN
729 	1901-06-30 	South West 	568.5926000000 	592.7527500000 	1,032.6202000000 	647.7038000000 	751.1875000000 	678.8912400000 	NaN

I was trying to plot multiple columns as Y asix with hvPlot and use hover tooltip to show the value of the column when mouse hovering on the individual line for the column.

highlighted_years = ['1982-1983', '2008-2009', '2010-2011', '2019-2020', '2020-2021', '2021-2022', '2022-2023']

tooltips = [
    ("month-day", "@{Month-Day-3}{%m-%d}")
]

hover = HoverTool(tooltips=tooltips, formatters={"@{Month-Day-3}": "datetime"})

tickfmt = DatetimeTickFormatter(months="%b")

ihvplot = ipipeline.hvplot(x='Month-Day-3', y=highlighted_years,
                                xlabel='Month', ylabel='Cumulative Rainfall (mm)',
                                xformatter=tickfmt, tools=[hover], ylim=(0,1100), 
                                line_width=3, height=400, width=700, grid=True,
                                xticks=DatetimeTicker(desired_num_ticks=12))
ihvplot

I would like to show “month-day” and the “value” of the column when mouse is hovering on the individual line (column). I am assuming I will need to set up the tooltips to achieve this?

Without setting the hover tools you already get the information you are asking for.

import hvplot.pandas
import pandas as pd

df = pd.DataFrame({
    "xseries": pd.date_range("2022-01-01", "2022-01-03"),
    "yseries1": [2,4,3],
    "yseries2": [2,3,5],
    "some_label": ["A", "B", "C"]
})

plot = df.hvplot(x="xseries", y=["yseries1", "yseries2"])

import panel as pn

pn.panel(plot, sizing_mode="stretch_both").servable()

My guess is that you need to customize the hover values as described in Plotting with Bokeh — HoloViews v1.15.0.

Please write a bit more about what you are trying to achieve. :+1:

1 Like

Thanks @Marc .

Yes I need to customize the hover values. The “Month-Day-3” column is a datetime type but I only need to show month and day. So I do need to set up the hover tool and show the value of the column where the mouse is hovering on the individual line (column).

I did read through the HoloViews tutorial as you pointed, but I am still unable to figure out how to achieve this.

The trick is to use ‘value’ in the tooltip:

import hvplot.pandas
import numpy as np
import pandas as pd

index = pd.date_range("2020-07-01", "2021-07-01", freq="D")
data = np.random.random((index.size, 4)) + 10 * np.arange(4)[np.newaxis, :]
df = pd.DataFrame(data, index=index, columns=list("ABCD"))

# Using custom models
from bokeh.models import DatetimeTickFormatter, HoverTool

# https://docs.bokeh.org/en/2.4.1/docs/reference/models/formatters.html#datetimetickformatter
tickfmt = DatetimeTickFormatter(years="%m-%d", months="%m-%d")

tooltips = [
    ("Month-Day", "@index{%m-%d}"),
    ("value", "@value"),
    ("name", "@Variable")
]
hover = HoverTool(tooltips=tooltips, formatters={"@index": "datetime"})

df.hvplot(xformatter=tickfmt, tools=[hover])

Screenshot 2022-09-26 09.32.28

3 Likes

Can you share a minimal, reproducible example (MRE)?

In general, a complete script only with the essential code, which can be copied/pasted and immediately run as-is with no modifications. This is much more useful than snippets.

Hi, I’m facing the same issue. Below is the minimal reproducible example.

import pandas as pd
import hvplot.pandas
import numpy as np
from bokeh.models import HoverTool
import holoviews as hv
import hvplot
import panel as pn
import geoviews as gv
gv.extension('bokeh')

# Create a dummy DataFrame
np.random.seed(0)
n = 10000  # number of points
df = pd.DataFrame({
    'Longitude': np.random.uniform(-180, 180, n),
    'Latitude': np.random.uniform(-90, 90, n),
    'Timestamp': pd.date_range(start='2023-01-01', periods=n, freq='D'),
    'Value': np.random.rand(n) * 100  # This will represent 'selected_col'
})


selected_col = 'Value'
tile = hv.element.tiles.EsriNatGeo().opts(width=1000, height=650)
hover = HoverTool(tooltips=[
                            ("Timestamp","@Timestamp"),
                            ("(Longitude,Latitude)", "@Longitude, @Latitude"),
                            (selected_col,f"@{{selected_col}}{{0.0}}")
                        ], formatters={"@{Timestamp}": "datetime"})
plot = df.hvplot.points(
    'Longitude', 'Latitude',
    geo=True, 
    tiles=tile,
    tools=[hover],
    c=selected_col,
    cmap='Reds',
    colorbar=True,
    rasterize=True,
    dynamic=False,
)
hvplot.show(plot)

How to make hover cols work?
I have been struggling with this for days now. Please help!

1 Like

For some reason, “@value” & “@name” don’t work at all for the following dataset.

import holoviews as hv
import hvplot.pandas
import numpy as np
import panel as pn
import pandas as pd

from bokeh.models import HoverTool

# generation of random data
engagements = np.random.normal(50,10,12)
engagement_r8 = np.random.normal(2,0.30,12)
months = range(1,13)

df = pd.DataFrame({'Month':months,
                   'Engagements':engagements,
                   'Engagement R8': engagements})

cols = list(df.columns)
cols.remove('Month')

# hv setup 
feature = pn.widgets.Select(options=cols, name='Feature')

def plotter(feature):
  scope = df[['Month',feature]]
  return scope

# Bokeh Hovertool setup
tooltips = [("Month", "@Month"),
            ("value", "@value"),
            ("name", "@Variable")]

hover_tool = HoverTool(tooltips=tooltips)

# creation of hv GUI
df_i = hvplot.bind(plotter, feature).interactive()

df_i.hvplot.line(x='Month', y=feature).opts(tools=[hover_tool])

Reults in
Screenshot 2023-11-30 at 11.25.28 AM

It should reference @Engagements I think

Apparently, both @value & @Variable are dynamic and should adjust to any column fed into the graph.

# Bokeh Hovertool setup
tooltips = [("Month", "@Month"),
            ("value", "@value"),
            ("name", "@Variable")]

Perhaps that’s only for the vanilla hvplot call, not interactive

1 Like

Hi Andrew, Do you have solution for this?

That makes sense. Thank you.

Something along the lines of:
However, @x and @y is in mercator, so it’s not very helpful and I forgot how HoloViews/hvPlot does it internally. Also, timestamp is dropped upon rasterize.

Can you submit an issue on hvplot regarding this?

import pandas as pd
import hvplot.pandas
import numpy as np
from bokeh.models import HoverTool
import holoviews as hv
import hvplot
import panel as pn
import geoviews as gv
from holoviews.operation.datashader import rasterize

gv.extension("bokeh")

# Create a dummy DataFrame
np.random.seed(0)
n = 10000  # number of points
df = pd.DataFrame(
    {
        "Longitude": np.random.uniform(-180, 180, n),
        "Latitude": np.random.uniform(-90, 90, n),
        "Timestamp": pd.date_range(start="2023-01-01", periods=n, freq="D"),
        "Value": np.random.rand(n) * 100,  # This will represent 'selected_col'
    }
)
display(df)

selected_col = "Value"
tile = hv.element.tiles.EsriNatGeo().opts(width=1000, height=650)
hover = HoverTool(
    tooltips=[
        ("Timestamp", "@Timestamp"),
        ("(Longitude,Latitude)", "@x, @y"),
        (selected_col, f"@{{image}}{{0.0}}"),
    ]
)
plot = df.hvplot.points(
    "Longitude",
    "Latitude",
    hover_cols=["Timestamp"],
    geo=True,
    tools=[hover],
    c=selected_col,
    cmap="Reds",
    colorbar=True,
    dynamic=False,
)
hvplot.show(plot)

Looking at GeoViews internals, it uses CustomJSHover to handle Mercator projections.

import pandas as pd
import hvplot.pandas
import numpy as np
from bokeh.models import HoverTool
import holoviews as hv
import hvplot
import panel as pn
import geoviews as gv
from holoviews.operation.datashader import rasterize
from bokeh.models import CustomJSHover

gv.extension("bokeh")

# Create a dummy DataFrame
np.random.seed(0)
n = 10000  # number of points
df = pd.DataFrame(
    {
        "Longitude": np.random.uniform(-180, 180, n),
        "Latitude": np.random.uniform(-90, 90, n),
        "Timestamp": pd.date_range(start="2023-01-01", periods=n, freq="D"),
        "Value": np.random.rand(n) * 100,  # This will represent 'selected_col'
    }
)
display(df)

_hover_code = """
    const projections = Bokeh.require("core/util/projections");
    const {snap_x, snap_y} = special_vars
    const coords = projections.wgs84_mercator.invert(snap_x, snap_y)
    return "" + (coords[%d]).toFixed(4)
"""

selected_col = "Value"
tile = hv.element.tiles.EsriNatGeo().opts(width=1000, height=650)
hover = HoverTool(
    tooltips=[
        ("Timestamp", "@Timestamp"),
        ("(Longitude,Latitude)", "$x{custom}, $y{custom}"),
        (selected_col, f"@image{{0.0}}"),
    ],
    formatters={
        "$x": CustomJSHover(code=_hover_code % 0),
        "$y": CustomJSHover(code=_hover_code % 1),
    },
)
plot = df.hvplot.points(
    "Longitude",
    "Latitude",
    hover_cols=["Timestamp"],
    geo=True,
    tools=[hover],
    c=selected_col,
    cmap="Reds",
    colorbar=True,
    dynamic=False,
    rasterize=True,
)
hvplot.show(plot)

Still can’t get Timestamp to work though; would be great if you can report that to the hvplot issues.

I created an issue on hvplot.