0

The Problem:

I am trying to plot a series of polygons using HoloViews and make them interactive by including tooltips, but I can't get the tooltips to work properly. I am able to plot the polygons just fine, and I'm able to create the tooltips, but I don't know how to link the tooltip back to my original datasource.

The Data:

Here's a sample of the data I'm working with as a Pandas dataframe:

import numpy as np
import pandas

data_dict = {'depth': {12: np.array([143.31908832, 174.03133903, 165.65527066, 160.07122507,
         162.86324786, 155.88319088, 153.78917379, 152.39316239,
         152.39316239, 127.26495727, 131.45299145, 131.45299145,
         133.54700855, 152.39316239, 135.64102564, 144.01709402,
         156.58119658, 134.24501424]),
  13: np.array([169.84330484, 189.38746439, 178.91737892, 166.35327635]),
  14: np.array([173.33333333, 187.29344729, 190.08547009, 176.12535613]),
  15: np.array([199.15954416, 211.72364672, 211.72364672, 199.15954416]),
  16: np.array([179.61538462, 194.27350427, 194.27350427, 179.61538462])},
 'ping_time': {12: np.array(['2017-06-28T22:47:51.213500000', '2017-06-28T22:48:37.570900000',
         '2017-06-28T22:49:46.736800000', '2017-06-28T22:50:41.866800000',
         '2017-06-28T22:51:17.024100000', '2017-06-28T22:51:24.038300000',
         '2017-06-28T22:51:48.138600000', '2017-06-28T22:52:17.414900000',
         '2017-06-28T22:52:58.491200000', '2017-06-28T22:53:02.007000000',
         '2017-06-28T22:52:18.584100000', '2017-06-28T22:51:47.552200000',
         '2017-06-28T22:51:22.284000000', '2017-06-28T22:51:16.439500000',
         '2017-06-28T22:50:40.112400000', '2017-06-28T22:49:45.568000000',
         '2017-06-28T22:48:36.390400000', '2017-06-28T22:47:57.668800000'],
        dtype='datetime64[ns]'),
  13: np.array(['2017-06-28T22:46:27.321600000', '2017-06-28T22:46:27.321600000',
         '2017-06-28T22:47:07.220500000', '2017-06-28T22:47:04.293000000'],
        dtype='datetime64[ns]'),
  14: np.array(['2017-06-28T22:43:49.614200000', '2017-06-28T22:43:49.614200000',
         '2017-06-28T22:44:02.499000000', '2017-06-28T22:44:02.499000000'],
        dtype='datetime64[ns]'),
  15: np.array(['2017-06-28T22:44:30.584400000', '2017-06-28T22:44:30.584400000',
         '2017-06-28T22:45:14.003600000', '2017-06-28T22:45:14.003600000'],
        dtype='datetime64[ns]'),
  16: np.array(['2017-06-28T23:02:20.571500000', '2017-06-28T23:02:20.571500000',
         '2017-06-28T23:02:58.034900000', '2017-06-28T23:02:58.034900000'],
        dtype='datetime64[ns]')},
 'region_id': {12: 14,
  13: 15,
  14: 16,
  15: 17,
  16: 18}}

data_df = pandas.DataFrame.from_dict(data_dict)

What I Have So Far:

And here's the code I'm using to generate the plot and tooltips:

warnings.filterwarnings("ignore", category=DeprecationWarning)
hv.extension('bokeh')

tooltips = [
    ('RegionID', '@region_id')
]

hover = HoverTool(tooltips=tooltips)

region_poly = hv.Polygons([np.array(list(zip(x,y))) for x, y in zip(data_df.ping_time,data_df.depth)]).opts(tools=[hover], alpha=0.3, color='r')

region_poly

The resulting plot looks like this:

Screenshot of the plotted image with tooltip.

In this screenshot, I am hovering over one of the polygons, causing it's tooltip to appear. You can see, however, that no data actually appears in the tooltip. Instead of showing the region_id value like I wanted, it's just showing "???".

My Question...

How can I get the region_id values corresponding to each polygon to appear in the tooltips?

Rory McGuire
  • 151
  • 9
  • Here you are asking Bokeh to show the "region_id" column when you hover, but you constructed the Polygons object without any region ID information, so Bokeh prints "???". data_dict and data_df do have the region_id, but neither of those have been provided to Polygons; instead you extracted the depth and time and passed in only those values, not the data_dict or data_df or the region ID as a separate bit of data. – James A. Bednar Jun 03 '22 at 16:49
  • Unfortunately, the [Polygons reference](https://holoviews.org/reference/elements/bokeh/Polygons.html) doesn't have any examples of how to work with data directly in a DataFrame, where such extra columns can be discovered automatically. – James A. Bednar Jun 03 '22 at 16:49
  • 1
    You should be able to adapt the examples that it does show to work, but it's a bit painful; I can just say that the key is that you need to provide `vdims='region_id"` while also providing the actual region ID data when constructing the object (as is done in those examples). – James A. Bednar Jun 03 '22 at 16:49

1 Answers1

1

I found an imperfect solution that creates some new issues but solves the original problem. I'm posting this as an answer rather than making an edit to my question because it technically solves my original problem, and I don't want to make the question overly long. I'll start by demonstrating the solution, then I'll explain the issues that remain in case anyone can come up with a better solution.

The Solution:

I've updated the dataframe to have an additional column. I'll use this to show how to add multiple items to a tooltip. The additional column is called "notes" and contains strings of variable length.

data_dict = {
    'depth': {
        12: np.array([143.31908832, 174.03133903, 165.65527066, 160.07122507,
                      162.86324786, 155.88319088, 153.78917379, 152.39316239,
                      152.39316239, 127.26495727, 131.45299145, 131.45299145,
                      133.54700855, 152.39316239, 135.64102564, 144.01709402,
                      156.58119658, 134.24501424]),
        13: np.array([169.84330484, 189.38746439, 178.91737892, 166.35327635]),
        14: np.array([173.33333333, 187.29344729, 190.08547009, 176.12535613]),
        15: np.array([199.15954416, 211.72364672, 211.72364672, 199.15954416]),
        16: np.array([179.61538462, 194.27350427, 194.27350427, 179.61538462])
    },
    'ping_time': {
        12: np.array(['2017-06-28T22:47:51.213500000', '2017-06-28T22:48:37.570900000',
                      '2017-06-28T22:49:46.736800000', '2017-06-28T22:50:41.866800000',
                      '2017-06-28T22:51:17.024100000', '2017-06-28T22:51:24.038300000',
                      '2017-06-28T22:51:48.138600000', '2017-06-28T22:52:17.414900000',
                      '2017-06-28T22:52:58.491200000', '2017-06-28T22:53:02.007000000',
                      '2017-06-28T22:52:18.584100000', '2017-06-28T22:51:47.552200000',
                      '2017-06-28T22:51:22.284000000', '2017-06-28T22:51:16.439500000',
                      '2017-06-28T22:50:40.112400000', '2017-06-28T22:49:45.568000000',
                      '2017-06-28T22:48:36.390400000', '2017-06-28T22:47:57.668800000'],dtype='datetime64[ns]'),
        13: np.array(['2017-06-28T22:46:27.321600000', '2017-06-28T22:46:27.321600000',
                      '2017-06-28T22:47:07.220500000', '2017-06-28T22:47:04.293000000'],dtype='datetime64[ns]'),
        14: np.array(['2017-06-28T22:43:49.614200000', '2017-06-28T22:43:49.614200000',
                      '2017-06-28T22:44:02.499000000', '2017-06-28T22:44:02.499000000'],dtype='datetime64[ns]'),
        15: np.array(['2017-06-28T22:44:30.584400000', '2017-06-28T22:44:30.584400000',
                      '2017-06-28T22:45:14.003600000', '2017-06-28T22:45:14.003600000'],dtype='datetime64[ns]'),
        16: np.array(['2017-06-28T23:02:20.571500000', '2017-06-28T23:02:20.571500000',
                      '2017-06-28T23:02:58.034900000', '2017-06-28T23:02:58.034900000'],dtype='datetime64[ns]')
    },
    'region_id': {
        12: 14,
        13: 15,
        14: 16,
        15: 17,
        16: 18
    },
    'notes': {
        12: 'abcdefg',
        13: 'hijklmn',
        14: 'op qrs,',
        15: 't u v w',
        16: 'x y-- z'
    }
}

data_df = pandas.DataFrame.from_dict(data_dict)

By defining the dimensions of each polygon within dictionaries r, I can add as many dimensions (i.e. dataframe columns) as I want beyond just and an x and a y.

def plot_polygons(df):
    hv.extension('bokeh')
    
    tooltips = [
        ('RegionID', '@v1'),
        ('Notes', '@v2')
    ]
    
    hover = HoverTool(tooltips=tooltips)
    
    region_list = []
    for index, row in df.iterrows():
        r = {
            'x': row['ping_time'].tolist(), 
            'y': row['depth'].tolist(), 
            'v1': row['region_id'],
            'v2': row['notes']
        }

        region_list.append(r)
    
    region_poly = hv.Polygons(region_list, vdims=['v1', 'v2']).opts(tools=[hover], alpha=0.3, color='r')

    return region_poly

plot_polygons(df=data_df)

This will yield a new plot with a functional tooltip.

Screenshot of polygons with functional tooltip

The Issues:

This strategy has two obvious issues.

  1. Each polygon is now a different color. And specifying a single color in .opts() doesn't seem to do anything now.
  2. Using row['ping_time'].tolist() seems to change the datetime values in the x axis to timestamp int values. Several suggestions to get around this can be found here, but I haven't figured out how to successfully implement any of them in my specific code.
Rory McGuire
  • 151
  • 9