0

I have my data set up similar to the fruits example below. I have been trying to add a slider that controls for example the year '2015' and when I change the slider value, the stacked chart updates according to this new value.

Example: Current value: 2015:[2, 1, 4, 3, 2, 4] Slider value: 2 New value = Current value + slider value ---> 2015: [4,3,6,5,4,6]

Slider value:0 ---> 2015:[2, 1, 4, 3, 2, 4] (back to original value)

I hope someone can help me.

Thank you!

from bokeh.palettes import GnBu3, OrRd3
output_notebook()

years = ['2015', '2016', '2017']

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

p = figure(x_range=fruits, plot_height=250, y_range=(0, 16), title="Fruit import/export, by year")

p.vbar_stack(years, x='fruits', width=0.9, color=GnBu3, source=ColumnDataSource(exports))


p.x_range.range_padding = 0.1
p.xgrid.grid_line_color = None

slider = Slider(start=0, end=5, value=0, step=1, title="2015 exports")

show(column(p,slider))
Ryan Zhang
  • 1,856
  • 9
  • 19
AxoBlue
  • 11
  • 3

2 Answers2

1

Here's a solution that works by replacing the whole object.

def plot_stack_slider():
    years = ['2015', '2016', '2017']
    fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
    exports = {'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={
               'fruits' : fruits,
               '2015'   : [2, 1, 4, 3, 2, 4],
               '2016'   : [5, 3, 4, 2, 4, 6],
               '2017'   : [3, 2, 4, 4, 5, 3]
              })
    display = ColumnDataSource(data={'fruits' : fruits,
               '2015'   : [2, 1, 4, 3, 2, 4],
               '2016'   : [5, 3, 4, 2, 4, 6],
               '2017'   : [3, 2, 4, 4, 5, 3]
              })

    p = figure(x_range=fruits, plot_height=250, y_range=(0, 16), title="Fruit import/export, by year")

    p.vbar_stack(years, x='fruits', width=0.9, color=GnBu3, source=display)
    # p.Bar(test_display,fruits,stacked=True)

    p.x_range.range_padding = 0.1
    p.xgrid.grid_line_color = None

    slider = Slider(start=0, end=5, value=0, step=1, title="2015 exports")
    slider2 = Slider(start=0, end=5, value=0, step=1, title="2016 exports")

    callback = CustomJS(
        args=dict(source=source, display=display, slider=slider, slider2=slider2),
        code="""
        display.data = { 
            ...source.data, 
            '2015': source.data['2015'].map((c) => c + slider.value ),
            '2016': source.data['2016'].map((c) => c + slider2.value ),
        };
    
    """)

    slider.js_on_change('value', callback)
    slider2.js_on_change('value', callback)
    
    show(column(p,slider, slider2))

plot_stack_slider()
AxoBlue
  • 11
  • 3
0

This should be possible with a CustomJS block and an aditional row to store the original value.

from bokeh.plotting import figure, show, output_notebook
from bokeh.models import CustomJS,ColumnDataSource, Slider
from bokeh.layouts import column
from bokeh.palettes import GnBu3, OrRd3
output_notebook()

years = ['2015', '2016', '2017']
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
exports = {'fruits' : fruits,
           '2015'   : [2, 1, 4, 3, 2, 4],
           '2015_org'   : [2, 1, 4, 3, 2, 4],
           '2016'   : [5, 3, 4, 2, 4, 6],
           '2017'   : [3, 2, 4, 4, 5, 3]
          }

source = ColumnDataSource(exports)

p = figure(x_range=fruits, plot_height=250, y_range=(0, 16), title="Fruit import/export, by year")

p.vbar_stack(years, x='fruits', width=0.9, color=GnBu3, source=source)

p.x_range.range_padding = 0.1
p.xgrid.grid_line_color = None

slider = Slider(start=0, end=5, value=0, step=1, title="2015 exports")

callback = CustomJS(
    args=dict(source=source, slider=slider),
    code="""
    let y = source.data['2015'];
    let y_org = source.data['2015_org']
    for (let i = 0; i < y.length; i++) {
        y[i] = y_org[i] + slider.value
    }

    source.change.emit();

""")

slider.js_on_change('value', callback)

show(column(p,slider))
mosc9575
  • 5,618
  • 2
  • 9
  • 32
  • Thank you! It works when I use the slider the first time, after that, it doesn't update the stacked values anymore. Do you have any idea why this might be happening? When I look at the log console, the values are correctly changing. – AxoBlue Aug 10 '22 at 18:01
  • It is very interessting that this happens on your machine, too. I have seen the same behavior and opened a [GitHub Issue](https://github.com/bokeh/bokeh/issues/12297). Right now I have no explanation. – mosc9575 Aug 11 '22 at 06:13
  • if we replace the whole object, it works. I don't understand why. See below: – AxoBlue Aug 12 '22 at 17:46
  • I tried your solution and it would work also with only one source. At the moment I don't understand. But I am happy that you got a working solution. – mosc9575 Aug 13 '22 at 09:20