如何使用JavaScript回调和Bokeh滑块更改垃圾箱宽度

时间:2018-07-05 15:40:30

标签: javascript python bokeh

任何人都可以提供有关如何使用Bokeh滑块对象更改Bokeh直方图的bin宽度的帮助。我知道如何使用Python回调执行此操作,但想知道如何使用JavaScript回调进行此操作-使用CustomJS函数。

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 ....

非常感谢您的帮助。

谢谢

1 个答案:

答案 0 :(得分:0)

为此,我的解决方案是使用原始数据(在您的情况下为x)重新计算customjs中的直方图。我对javascript真的很陌生,因此可能会有更优雅的方法来实现这一点。但是无论如何,该代码仍对我有用,因此您可以尝试一下。

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))