Selection of nested Bars

Hello im trying to get the selected column of a bar chart with nested categorical groups, but i dont understand what the returned index of the tapstream means.

With this code here (mostly taken from http://holoviews.org/reference/elements/bokeh/Bars.html )

import numpy as np
from holoviews import opts
import panel as pn
import numpy as np
import holoviews as hv
import pandas as pd

hv.extension('bokeh')

samples = 100

pets = ['Cat', 'Dog', 'Hamster', 'Rabbit']
genders = ['Female', 'Male']

pets_sample = np.random.choice(pets, samples)
gender_sample = np.random.choice(genders, samples)
count = np.random.randint(1, 5, size=samples)

df = pd.DataFrame({'Pets': pets_sample, 'Gender': gender_sample, 'Count': count})
bars = hv.Bars(df, kdims=['Pets', 'Gender']).aggregate(function=np.sum).opts(opts.Bars(tools=["hover", "tap"]))
stream = hv.streams.Selection1D(source=bars)
bars.opts(width=500)

When i tap on a bar (ex.: dog,male), the stream.contents are:

{'index': [4]}

which does not respond to the bars.data dataframe

    idx Pets 	    Gender 	Count
    0 	Dog 	    Female 	18
    1 	Hamster 	Female 	29
    2 	Dog 	    Male 	47
    3 	Cat 	    Female 	33
    4 	Rabbit 	    Male 	31
    5 	Cat 	    Male 	34
    6 	Rabbit 	    Female 	38
    7 	Hamster 	Male 	9

For some bars, for example (hamster,female) the stream.contents show the correct index though.

Can someone help me explain whats happening here?

So apparently the index value comes from the underlying bokeh columndata source.

Im unsure of the best way to access it, so i used a hook and access it as a closure.

import numpy as np
from holoviews import opts
import panel as pn
import numpy as np
import holoviews as hv
import pandas as pd

pn.extension()
hv.extension('bokeh')

samples = 100

pets = ['Cat', 'Dog', 'Hamster', 'Rabbit']
genders = ['Female', 'Male']

pets_sample = np.random.choice(pets, samples)
gender_sample = np.random.choice(genders, samples)
count = np.random.randint(1, 5, size=samples)

df = pd.DataFrame({'Pets': pets_sample, 'Gender': gender_sample, 'Count': count})

closure_map = {}
def cds_swap_hook(plot, element):
    renderer = plot.handles['glyph_renderer']
    closure_map["data_source"] = renderer.data_source

    
bars = hv.Bars(df, kdims=['Pets', 'Gender']).aggregate(function=np.sum).opts(opts.Bars(tools=["hover", "tap"], hooks=[cds_swap_hook])).opts(width=500, axiswise=True)
stream = hv.streams.Selection1D(source=bars)

def tap_cb(index):
    if not index:
        return hv.Bars(df, kdims=['Pets', 'Gender']).select(Pets="Cat", Gender="Female")
    selected_gender = closure_map["data_source"].data["Gender"][index][0]
    selected_pet = closure_map["data_source"].data["Pets"][index][0]

    return hv.Bars(df, kdims=['Pets', 'Gender']).select(Pets=selected_pet, Gender=selected_gender)

dependend_dmap = hv.DynamicMap(tap_cb, streams=[stream])

pn.Column(
    bars,
    dependend_dmap
)

This solves my problem of accessing the selected data, but it feels kinda hacky.

As a result i can show a drilled down plot of the selected data.

Posting this so maybe someone can get a use of it :slight_smile: