Stream data to a hexbin plot using a ColumnDataSource

Hi, I am trying to plot a hexbin plot, which would act as a heatmap, and want to update it every few seconds with new data. Right now I have this, serving as an MRE:

import numpy as np
from bokeh.models import HoverTool, ColumnDataSource
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource

n = 500
x = np.random.normal(size = n)
y = np.random.normal(size = n)

source = ColumnDataSource(dict(x = x, y = y))

p = figure(title="Hexbin for 500 points", match_aspect=True,
           tools="wheel_zoom,reset,pan", background_fill_color='#440154')
p.grid.visible = False

r, _ = p.hexbin(x, y, size=0.1, hover_color="pink", hover_alpha=0.8)

p.add_tools(HoverTool(
    tooltips=[("count", "@c"), ("(q,r)", "(@q, @r)")],
    mode="mouse", point_policy="follow_mouse", renderers=[r],
))

def update():
    global x, y, p, r
    p.renderers.remove(r)
    x = np.random.normal(size = n)
    y = np.random.normal(size = n)
    r, _ = p.hexbin(x, y, size=0.1, hover_color="pink", hover_alpha=0.8)

curdoc().add_periodic_callback(update, 1000)
curdoc().add_root(p)

This does the well for this sized data, however, for my data, the “blinking” which happens everytime the update function is called through the periodic callback is undesirable. Furthermore, it also plots the hexbins over any existing glyphs that are already on my plot which is also undesirable. As such, I was wondering if there was any way to stream data to a ColumnDataSource, as one would do with a scatter plot, and have that act as my stream instead. The code below shows what I would like to be able to do:

import numpy as np
from bokeh.models import HoverTool, ColumnDataSource
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource

n = 500
x = np.random.normal(size = n)
y = np.random.normal(size = n)

source = ColumnDataSource(dict(x = [], y = []))

p = figure(title="Hexbin for 500 points", match_aspect=True,
           tools="wheel_zoom,reset,pan", background_fill_color='#440154')
p.grid.visible = False

r, _ = p.hexbin("x", "y", size=0.1, hover_color="pink", hover_alpha=0.8, source = source)

p.add_tools(HoverTool(
    tooltips=[("count", "@c"), ("(q,r)", "(@q, @r)")],
    mode="mouse", point_policy="follow_mouse", renderers=[r],
))

def update():
    global x, y, p, r
    p.renderers.remove(r)
    x = np.random.normal(size = n)
    y = np.random.normal(size = n)
    source.stream(dict(x=x, y=y))

curdoc().add_periodic_callback(update, 1000)
curdoc().add_root(p)

I do realize that this may be a futile question as the docs indicate that the hexbin function does not take a ColumnDataSource as an argument but I was wondering if anyone had any ideas on a workaround for this?

Thanks

figure.hexbin is one of only a handful of methods that actually do some data processing and transformation for you. If you want to update an existing hex plot you are going to need to use the lower level hex_tile glyph that hexbin uses under the covers directly, and to update it, you’ll need to the the helper function bokeh.util.hexbin that does the binning and returns the q and r coordinates that a hex tiling expects.

The very short implemention of figure.hexbin is the best reference. You’ll basically just want to do this yourself, instead of using figure.hexbin, so that you can later update the q and r columns of the hex_tile CDS in place.

Hi Bryan, thank you so much this was exactly what I was looking for but could not find it in the docs.

Thanks

Jack

1 Like