1

Could anyone provide help on how to change the bin widths of a Bokeh histogram plot using a Bokeh slider object. I know how to do this using a Python callback but would like to know how to do this using a JavaScript callback - using the CustomJS function.

import pandas as pd
import numpy as np
from bokeh.plotting import figure
from bokeh.models.widgets import Slider
from bokeh.models import ColumnDataSource, CustomJS

# generate random data
x = np.random.choice(a = 20, size = 100, replace = True)

# generate histogram
hist, edges = np.histogram(x, bins = 10)

# create dataframe of histogram
hist_df = pd.DataFrame({'count': hist, 'left':edges[:-1], 'right':edges[1:]})

# generate bokeh data source
bokeh_data = ColumnDataSource(hist_df)

# generate bokeh plot
plot = figure()
plot.quad(source = bokeh_data, bottom = 0, top = 'counts', left = 'left', right = 'right') 

# generate slider object to change bin widths interactively
bin_slider = Slider(start = 10,  end = 100, step = 5, value = 10)
bin_slider.js_on_change('value', callback)

# javascript callback function to change bin widths goes here...
callback = CustomJS ....

Any help is greatly appreciated.

Thanks

Daniel
  • 11
  • 1
  • Using `CustomJS` means you will have to re-compute the histogram itself in JavaScript (NumPy does not exist in your browser). You might find useful information in https://stackoverflow.com/questions/8195820/histogram-in-javascript – bigreddot Jul 05 '18 at 17:01

1 Answers1

0

My solution for this is to use the original data (x in your case) for recalculating the histogram within the customjs. I am really new to javascript so there may be more elegant way to implement this. But the code works for me anyway so you can give it a try.

import pandas as pd
import numpy as np
from bokeh.plotting import figure
from bokeh.models.widgets import Slider, RangeSlider
from bokeh.models import ColumnDataSource, CustomJS

# generate random data
x = np.random.normal(10, 1, 1000)

# generate histogram
hist, edges = np.histogram(x, bins = 10)

# create dataframe of histogram
hist_df = pd.DataFrame({'count': hist, 'left':edges[:-1], 'right':edges[1:]})

# generate bokeh data source for initial histogram
bokeh_data = ColumnDataSource(hist_df)

# generate bokeh data source for new histogram calculation
x_df=pd.DataFrame(x,columns={'value'})

x_src=ColumnDataSource(x_df)


# generate bokeh plot
plot = figure()
plot.quad(source = bokeh_data, bottom = 0, top = 'count', left = 'left', right = 'right') 

callback = CustomJS(args=dict(source1=x_src,source2=bokeh_data), code="""
    var data = source1.data;
    var val = data['value'];
    var length = val.length;
    var size  = bin_size.value;
    var min = data_range.value[0];
    var max = data_range.value[1];

    // Decide number of bins needed
    var bins = Math.floor((max - min) / size); 

    // Put left edge point in an array
    var left_edge = new Array(bins);
    for (var i = 0; i < bins; i++){
        left_edge[i] = min+i*size;
    }

    // Put right edge point in an array
    var right_edge = new Array(bins);
    for (var i = 0; i < bins; i++){
        right_edge[i] = min+(i+1)*size;
    }

    // Initialize frequency
    var frequency = new Array(bins);
    for (var i = 0; i < bins; i++) frequency[i] = 0;

    // Calculate frequency for each bin
    for (var i = 0; i < length; i++) {
        if (val[i]==min) frequency[0]++;
        else if (val[i]==max) frequency[bins-1]++;
        else frequency[Math.floor((val[i] - min) / size)]++;

    }

    // Oupdate data source with new bins and frequency
    var bokeh_data_new={};
    bokeh_data_new.count=frequency;
    bokeh_data_new.left=left_edge;
    bokeh_data_new.right=right_edge;

    source2.data=bokeh_data_new;
""")

# generate slider object to change bin widths interactively
binwidth_slider = Slider(start = 0,  end = 1, step = 0.02, value = 0.5 , callback=callback)
callback.args["bin_size"] = binwidth_slider

# generate range slider object to change min and max of the data points to be shown interactively
range_slider = RangeSlider(start = 0, end = 20,  step = 1, value = (0, 20), callback=callback)
callback.args["data_range"] = range_slider

widgets = WidgetBox(binwidth_slider, range_slider)

output_notebook()

show(row(plot, widgets))
Sam__
  • 1
  • 1