☰Read-only archive⌕
Looking for help with Bokeh?

This is a read-only archive. Ask questions and join the conversation on GitHub.

Visit Community Support on GitHub →
Archived discussions · Captured October 28, 2026

Is it possible to use javascript to fill histograms from linked scatterplots?

Community Support
richardxdubois
richardxdubois

I’ve seen nice examples for linking a filled scatterplot to an empty one + datatable, filling the latter upon selection of the filled scatterplot. That was done with javascript.

What I’d like to do is histogram those selected points. When I make histograms, I use the example of using numpy.histogram and then feed a vbar. That requires a server app to go back to python.

Is there a way to fill a histogram from JavaScript, and hence be standalone?

Thanks!

Richard

⌁   Link to post
Bryan
Bryan

It’s certainly possible, but you would have to do all the binning yourself. I am not personally aware of JS code I can point you at. If you know the bins you want up front, things will be much simpler (you can define the vbar plot up front, and binning is only a matter of looping of the data and incrementing the corresponding bin count). If you need dynamic bins, that’s going to be fairly more involved.

⌁   Link to post
richardxdubois
richardxdubois

Thanks, Bryan. I can ask around of some of my colleagues if they’re aware of any binning code. They do a lot of data display in a different framework. Good question on the dynamic binning.

I was “afraid” the answer would be it’s on me. :wink:

That said, the linked plots example I saw may provide enough enthusiasm for me to add to my server app for a telescope project to select focal plane pixels and histogram values for those pixels. Something to add to the shelter in place todo list.

Richard

⌁   Link to post
richardxdubois
richardxdubois

Here’s a test script that does it: (pardon the ugly formatting - the boldface text were comments.

# Generate linked plots + TABLE displaying data + save button to export cvs of selected data

from random import random
import numpy as np

from bokeh.io import show

from bokeh.layouts import row
from bokeh.layouts import grid
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.models import Button  # for saving data
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn, Div
from bokeh.models import HoverTool
from bokeh.plotting import figure


# create data
x = [random() for x in range(500)]
y = [random() for y in range(500)]

# create first subplot
plot_width = 400
plot_height = 400

s1 = ColumnDataSource(data=dict(x=x, y=y))
fig01 = figure(
    plot_width=plot_width,
    plot_height=plot_height,
    tools=["box_select", "reset", "save"],
    title="Select Here",
)
fig01.circle("x", "y", source=s1, alpha=0.6)

# create second subplot
s2 = ColumnDataSource(data=dict(x=[], y=[]))

# demo smart error msg:  `box_zoom`, vs `BoxZoomTool`
fig02 = figure(
    plot_width=400,
    plot_height=400,
    x_range=(0, 1),
    y_range=(0, 1),
    tools=["box_zoom", "wheel_zoom", "reset", "save"],
    title="Watch Here",
)

fig02.circle("x", "y", source=s2, alpha=0.6, color="firebrick")

# demo smart error msg:  `box_zoom`, vs `BoxZoomTool`
fig03 = figure(
    plot_width=400,
    plot_height=400,
    x_range=(0, 1),
    tools=["box_zoom", "wheel_zoom", "reset", "save"],
    title="Watch Histogram Here",
)

hist, bins = np.histogram(x, bins=20)
source_hist = ColumnDataSource(data=dict(hist=hist, bins=bins[0:-1]))

fig03.vbar(top="hist", x="bins", width=0.05, source=source_hist, alpha=0.6, color="firebrick")

# create dynamic table of selected points
columns = [
    TableColumn(field="x", title="X axis"),
    TableColumn(field="y", title="Y axis"),
]

table = DataTable(
    source=s2,
    columns=columns,
    width=400,
    height=600,
    sortable=True,
    selectable=True,
    editable=True,
)

# fancy javascript to link subplots
# js pushes selected points into ColumnDataSource of 2nd plot
# inspiration for this from a few sources:
# credit: https://stackoverflow.com/users/1097752/iolsmit via: https://stackoverflow.com/questions/48982260/bokeh-lasso-select-to-table-update
# credit: https://stackoverflow.com/users/8412027/joris via: https://stackoverflow.com/questions/34164587/get-selected-data-contained-within-box-select-tool-in-bokeh

# https://stackoverflow.com/questions/37445495/binning-an-array-in-javascript-for-a-histogram

s1.selected.js_on_change(
    "indices",
    CustomJS(
        args=dict(s1=s1, s2=s2, s3=source_hist, table=table),
        code="""
        var inds = cb_obj.indices;
        var d1 = s1.data;
        var d2 = s2.data;
        var dhist = s3.data;
        var bhist = dhist['bins'];
        // get the selected items and push them into the s2 CDS
        d2['x'] = []
        d2['y'] = []
        for (var i = 0; i < inds.length; i++) {
            d2['x'].push(d1['x'][inds[i]])
            d2['y'].push(d1['y'][inds[i]])
        }
        s2.change.emit();
        table.change.emit();

        // grab the x values to re-histogram
        var dh = []
        for (var i = 0; i < inds.length; i++) {
            dh.push(d1['x'][inds[i]])
        }
        // get the histogram numbins, bin width from initial histogram
        var bins = [];
        var binCount = 0;
        var interval = bhist[1] - bhist[0];
        var numOfBuckets = bhist.length;
        
        //Setup Bins
        for(var i = 0; i < numOfBuckets; i += interval){
          bins.push({
            binNum: binCount,
            minNum: bhist[i],
            maxNum: bhist[i] + interval,
            count: 0
          })
          binCount++;
        }
        //Loop through data and add to bin's count
        for (var i = 0; i < dh.length; i++){
          var item = dh[i];
          var a = (item - bhist[0])/interval;
          var j = Math.floor(a);
          var bin = bins[j];
          bin.count++;
          }  
        // push the bin values back into the original histogram]]]}
        dhist['hist'] = []
        for (var j = 0; j < 20; j++) {
            dhist['hist'].push(bins[j].count)
        }
        s3.change.emit();
    """,
    ),
)

# create save button - saves selected datapoints to text file onbutton
# inspriation for this code:
# credit:  https://stackoverflow.com/questions/31824124/is-there-a-way-to-save-bokeh-data-table-content
# note: savebutton line `var out = "x, y\\n";` defines the header of the exported file, helpful to have a header for downstream processing

savebutton = Button(label="Save", button_type="success")
callback = CustomJS(
    args=dict(source_data=s1),
    code="""
        var inds = source_data.selected.indices;
        var data = source_data.data;
        var out = "x, y\\n";
        for (i = 0; i < inds.length; i++) {
            out += data['x'][inds[i]] + "," + data['y'][inds[i]] + "\\n";
        }
        var file = new Blob([out], {type: 'text/plain'});
        var elem = window.document.createElement('a');
        elem.href = window.URL.createObjectURL(file);
        elem.download = 'selected-data.txt';
        document.body.appendChild(elem);
        elem.click();
        document.body.removeChild(elem);
        """,
)
savebutton.js_on_click(callback)

# add Hover tool
# define what is displayed in the tooltip
tooltips = [
    ("X:", "@x"),
    ("Y:", "@y"),
    ("static text", "static text"),
]

fig02.add_tools(HoverTool(tooltips=tooltips))

# display results
# demo linked plots
# demo zooms and reset
# demo hover tool
# demo table
# demo save selected results to file

get_d3 = '<script src="https://d3js.org/d3.v5.min.js"></script>'
d3_div = Div(text=get_d3)


layout = grid([fig01, fig02, fig03, table, savebutton], ncols=3)

show(layout)
⌁   Link to post
gmerritt123
gmerritt123

I have successfully done this within bokeh by “importing” d3js into the script portion of the bokeh components. With that added, I get access to the d3js library in my callbacks, which I’ve used to do dynamic binning etc with standalone html/no server involved.

⌁   Link to post

End of archived discussion · Replies are disabled