I am working on a project to visualize a set of data using different diagrams. Using Bokeh's ColumnDataSource along with sliders for callbacks, I have set up a program that illustrates the data using bar charts and pie charts. I also want to add a Sankey diagram to illustrate the same data, where the plotted values change based on the value of the sliders. To do this I have to use HoloViews' Sankey module.
I have set up a javascript callback that automatically updates the values in the ColumnDataSource, and have linked those to the values the Sankey is meant to use. The issue I am having is that the Sankey diagram does not update in response to the value of the sliders changing, and I haven't been able to figure out why. If I add separate sliders from HoloViews, it ends up working. But this is not an ideal solution, as I want the Sankey to be able to interact with the sliders from Bokeh, and update itself in real-time.
There were not a lot of helpful resources online, but I suspect the issue can be resolved using the DynamicMap+streams modules. I just haven't been able to figure out how
Here is my code:
import holoviews as hv
hv.extension('bokeh')
from holoviews import opts, dim, streams, param
from bokeh.io import show
from bokeh.models import ColumnDataSource, TextInput
from bokeh.palettes import Paired12, Pastel1_3
from bokeh.plotting import figure
from bokeh.transform import factor_cmap
from bokeh.layouts import column, row
from bokeh.models import CustomJS, Slider, Dropdown, Panel, Tabs, HoverTool, Paragraph
fruits = ['El Biom', 'El CS', 'El NEE', 'Fuel Biom', 'Fuel CS',
'Fuel NEE', 'CU Biom', 'CU CS', 'CU NEE', 'NET Biom',
'NET CS', 'NET NEE']
counts = [0, 0.25, 0.425, 0.0261420833, 0.035854375, 0.079944375, 0.0368772727, 0.0426204545, 0.228336818,
0.0216474074, 0.0172977778, 0.0586568889]
empty = [0, 0.25, 0.425, 0.0261420833, 0.035854375, 0.079944375, 0.0368772727, 0.0426204545, 0.228336818,
0.0216474074, 0.0172977778, 0.0586568889]
source = ColumnDataSource(data=dict(fruits=fruits, counts=counts, empty=empty))
slider = Slider(start=0, end=2, value=1, step=.05, title="El Gen")
slider2 = Slider(start=0, end=2, value=1, step=.05, title="Fuel")
slider3 = Slider(start=0, end=2, value=1, step=.05, title="CU")
slider4 = Slider(start=0, end=2, value=1, step=.05, title="NET")
callback = CustomJS(args=dict(source=source, source_zer=source_zer, slider=slider, slider2=slider2, slider3=slider3, slider4=slider4),
code="""
let x = source.data['empty']
let y = source.data['counts'];
for (let i = 0; i < y.length; i++) {
x[0] = y[0] * slider.value
x[1] = y[1] * slider.value
x[2] = y[2] * slider.value
x[3] = y[3] * slider2.value
x[4] = y[4] * slider2.value
x[5] = y[5] * slider2.value
x[6] = y[6] * slider3.value
x[7] = y[7] * slider3.value
x[8] = y[8] * slider3.value
x[9] = y[9] * slider4.value
x[10] = y[10] * slider4.value
x[11] = y[11] * slider4.value
}
source.change.emit();
""")
slider.js_on_change('value', callback)
slider2.js_on_change('value', callback)
slider3.js_on_change('value', callback)
slider4.js_on_change('value', callback)
data_zer = [
['Electricity', 'Biom', source.data['empty'][0]],
['Electricity', 'CS', source.data['empty'][1]],
['Electricity', 'NEE', source.data['empty'][2]],
['Fuel', 'Biom', source.data['empty'][3]],
['Fuel', 'CS', source.data['empty'][4]],
['Fuel', 'NEE', source.data['empty'][5]],
['CU', 'Biom', source.data['empty'][6]],
['CU', 'CS', source.data['empty'][7]],
['CU', 'NEE', source.data['empty'][8]],
['NET', 'Biom', source.data['empty'][9]],
['NET', 'CS', source.data['empty'][10]],
['NET', 'NEE', source.data['empty'][11]]]
sankey.opts(
opts.Sankey(height=500, width=750, cmap='Paired')
)
tab3 = Panel(child=hv.render(sankey), title="Sankey")
layout = row(column(slider, slider2, slider3, slider4), Tabs(tabs=[tab3]))
show(layout)