1
votes

I'm trying to create custom hover based on data on a stacked chart. In the example below, if user hovers over 'cat1', then 'cat1_text' should be returned; 'cat2_text' and 'cat3_text' for 'cat2' and 'cat3' accordingly.

For the tooltip, since $name will return either 'cat1','cat2', or 'cat3', I thought by adding '_text', the value will be called accordingly (but of course this seems not to be the way python/bokeh works). I was thinking about using any function/index calls as well, but not too sure how to do so. Kindly advice. Many thanks!

category = ['cat1', 'cat2', 'cat3']

data = {'timeVal' : [0,1,2,3,4,5],
        'cat1'   : [2, 1, 4, 3, 2, 4],
        'cat2'   : [5, 3, 4, 2, 4, 6],
        'cat3'   : [3, 2, 4, 4, 5, 3],
        'cat1_text'   : ['a','b','c','d','e','f'],
        'cat2_text'   : ['a1','b1','c1','d1','e1','f1'],
        'cat3_text'   : ['a2','b2','c2','d2','e2','f2'],
}

toolTipArr = [
    ("name", "$name"),
    ("count", "@$name"),
    ("info", '@'+'$name'+'_text}')
]

p = figure(x_range=(startTime,endTime), plot_height=250, plot_width=1000, title="project",
           toolbar_location="right", tools="hover,pan,wheel_zoom,box_zoom,reset", 
           tooltips=toolTipArr)

p.vbar_stack(category, x='timeVal', width=2, color=colors, source=data,
             legend=[value(x) for x in category])
2

2 Answers

2
votes

This is the implementation suggested by bigreddot using CustomJSHover (works for Bokeh v1.3.0):

from bokeh.core.properties import value
from bokeh.models import ColumnDataSource, CustomJSHover
from bokeh.plotting import figure, show

fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
years = ["2015", "2016", "2017"]
colors = ["#c9d9d3", "#718dbf", "#e84d60"]

data = {'fruits' : fruits,
        '2015'   : [2, 1, 4, 3, 2, 4],
        '2016'   : [5, 3, 4, 2, 4, 6],
        '2017'   : [3, 2, 4, 4, 5, 3]}

source = ColumnDataSource(data=data)

tooltips= [("name", "$name"), ("count", "@$name")]

p = figure(x_range=fruits, plot_height=350, title="Fruit Counts by Year",
           toolbar_location=None, tooltips=tooltips)

renderers = p.vbar_stack(years, x='fruits', width=0.9, color=colors, source=source,
                         legend=[value(x) for x in years], name=years)

p.hover[0].tooltips.append(('info', '$name{custom}'))
p.hover[0].formatters = {'$name' : CustomJSHover(code = "return special_vars.name + '_text'")}

show(p)

With a little bit more effort you could achieve the same using CustomJS callback (Bokeh v1.3.0):

from bokeh.core.properties import value
from bokeh.models import ColumnDataSource, HoverTool, CustomJS
from bokeh.plotting import figure, show

fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
years = ["2015", "2016", "2017"]
colors = ["#c9d9d3", "#718dbf", "#e84d60"]

data = {'fruits' : fruits,
        '2015'   : [2, 1, 4, 3, 2, 4],
        '2016'   : [5, 3, 4, 2, 4, 6],
        '2017'   : [3, 2, 4, 4, 5, 3]}

source = ColumnDataSource(data=data)

p = figure(x_range=fruits, plot_height=350, title="Fruit Counts by Year",
           toolbar_location=None, tools="")

renderers = p.vbar_stack(years, x='fruits', width=0.9, color=colors, source=source,
                         legend=[value(x) for x in years], name=years)

hover_code = "if (cb_data.index.indices.length > 0) { cb_obj.tooltips[2] = ['info', cb_obj.name + '_text'] }"

for renderer in renderers:
    p.add_tools (HoverTool(tooltips=[("name", "$name"),
                                     ("count", "@$name"),
                                     ("info", "@info"), ],
                           renderers=[renderer],
                           name = renderer.name,
                           callback = CustomJS(code = hover_code)))
show(p)

enter image description here

1
votes

$name and @$name are evaluated first before any other text operations, so that is why the approach above will not work. You will need to use CustomJSHover to make something like this work

custom = CustomJSHover(args=dict(source=source), code="""
    // use special_vars.name and use special_vars.indices to index in 
    // to the right column of source.data
""")