任何人都可以提供有关如何使用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 ....
非常感谢您的帮助。
谢谢
我的解决方案是使用原始数据(在您的情况下为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))