How to update hbar/vbar using bokeh server?

I have this code and I can update the time-series graph successfully but I am looking for a way to update the bar chart. Is there a way of doing that?

import numpy as np

np.random.seed(1)

from bokeh.layouts import row, column, gridplot
from bokeh.models import ColumnDataSource, Slider, Select
from bokeh.plotting import curdoc, figure
from bokeh.driving import count
from bokeh.models.glyphs import HBar

source = ColumnDataSource(dict(
    time=[], average=[], algo_name=[]
))

p2 = figure(plot_height=100, y_axis_location="right")
p2.line(x='time', y='average', color='red', source=source)

p3 = figure(plot_height=100, y_axis_location="right")
p3.hbar(y='algo_name', height=0.5, left=0, right='average', color="#CAB2D6", source=source, fill_alpha=0.3)


def _create_prices(t):
    mean = 0
    std = 0.04
    average = np.asarray(np.random.lognormal(mean, std, 1))
    return average[0]


@count()
def update(t):

    mean = 0
    std = 0.04
    average = np.asarray(np.random.lognormal(mean, std, 1))
    algo_name = 1
    new_data = dict(
        time=[t],
        average=[average],
        algo_name=[algo_name]
    )
    source.stream(new_data, 100)
    

series = column(p2, p3)

curdoc().add_root(series)
curdoc().add_periodic_callback(update, 1000)
curdoc().title = "OHLC"

I was able to solve the problem using patches.

1 Like

I’m not sure the issue you had but for posterity, it’s definitely possible to update vbars in exactly the same way as all other glyphs.

I used patches to update the vbar. Using stream will not delete the previous bar so the new bar will be drawn above the old bar and keep doing that.

Here is my code, I don’t know if there is a better way of doing this:

import numpy as np

np.random.seed(1)

from bokeh.layouts import row, column, gridplot
from bokeh.models import ColumnDataSource, Slider, Select
from bokeh.plotting import curdoc, figure
from bokeh.driving import count
from bokeh.models.glyphs import HBar

source = ColumnDataSource(dict(
    time=[], average=[], algo_name=[]
))

bar_source = ColumnDataSource(dict(average=[0], algo_name=[1]))

p2 = figure(plot_height=100, y_axis_location="right")
p2.line(x='time', y='average', color='red', source=source)

p3 = figure(plot_height=100, y_axis_location="right", x_range=(0, 2))
p3.hbar(y='algo_name', height=0.5, left=0, right='average', color="#CAB2D6", source=bar_source, fill_alpha=0.9)


# p3.patch(x='algo_name', height=0.5, left=0, right='average', color="#CAB2D6", source=source, fill_alpha=0.3)


def _create_prices(t):
    mean = 0
    std = 0.04
    average = np.asarray(np.random.lognormal(mean, std, 1))
    return average[0]


@count()
def update(t):
    mean = 0
    std = 0.04
    average = np.asarray(np.random.lognormal(mean, std, 1))
    algo_name = 1


    # new_x = source.data['x'][s] + np.random.uniform(-0.1, 0.1, size=100)
    # new_y = source.data['y'][s] + np.random.uniform(-0.2, 0.2, size=100)
    # source.patch({'x': [(s, new_x)], 'y': [(s, new_y)]})
    if t>0:
        bar_source.patch({'average': [(0, average)]})

    new_data = dict(
        time=[t],
        average=[average],
        algo_name=[algo_name]
    )
    source.stream(new_data, 100)


series = row(p2, p3)

curdoc().add_root(series)
curdoc().add_periodic_callback(update, 1000)
curdoc().title = "OHLC"

@Mohamed_Ibrahim When you said “patches” I thought you meant the Patches glyph, not the source.patch method. In any event yes that seems correct. source.stream is specifically for adding data, it will never update anything previous. So if you want to update things, you should either use source.patch or else you can always just replace source.data entirely with a new data dict.

1 Like