from bokeh.io import output_notebook, show
from bokeh.plotting import figure
output_notebook()
前面展示了一个画布上展示多张图。
其实这些图片也可以有联动关系。
展示相同位置
from bokeh.layouts import gridplot
x = list(range(11))
y0, y1, y2 = x, [10-i for i in x], [abs(i-5) for i in x]
plot_options = dict(width=250, plot_height=250, tools='pan,wheel_zoom')
# create a new plot
s1 = figure(**plot_options)
s1.circle(x, y0, size=10, color="navy")
# create a new plot and share both ranges
s2 = figure(x_range=s1.x_range, y_range=s1.y_range, **plot_options)
s2.triangle(x, y1, size=10, color="firebrick")
# create a new plot and share only one range
s3 = figure(x_range=s1.x_range, **plot_options)
s3.square(x, y2, size=10, color="olive")
p = gridplot([[s1, s2, s3]])
# show the results
show(p)
如上代码, 它们的关联关系为wheel_zoom
, 这意味着其中几张图片拥有相同的坐标定位, 拉动其中一张图, 观察它的上下左右不同方向时, 其他几张图也会跟着改变左边的展示位置。
选择相同区域
from bokeh.models import ColumnDataSource
x = list(range(-20, 21))
y0, y1 = [abs(xx) for xx in x], [xx**2 for xx in x]
# create a column data source for the plots to share
source = ColumnDataSource(data=dict(x=x, y0=y0, y1=y1))
TOOLS = "box_select,lasso_select,help"
# create a new plot and add a renderer
left = figure(tools=TOOLS, width=300, height=300)
left.circle('x', 'y0', source=source)
# create another new plot and add a renderer
right = figure(tools=TOOLS, width=300, height=300)
right.circle('x', 'y1', source=source)
p = gridplot([[left, right]])
show(p)
上面样例就是在多个图中选择相同区域的标记。
鼠标悬停展示更多信息
from bokeh.models import HoverTool
source = ColumnDataSource(
data=dict(
x=[1, 2, 3, 4, 5],
y=[2, 5, 8, 2, 7],
desc=['A', 'b', 'C', 'd', 'E'],
)
)
hover = HoverTool(
tooltips=[
("index", "$index"),
("(x,y)", "($x, $y)"),
("desc", "@desc"),
]
)
p = figure(plot_width=300, plot_height=300, tools=[hover], title="Mouse over the dots")
p.circle('x', 'y', size=20, source=source)
show(p)
了解更多信息,点击这里
小工具
from bokeh.models.widgets import Slider
slider = Slider(start=0, end=10, value=1, step=.1, title="foo")
show(slider)
Bokeh支持直接加入小工具, 了解更多, 点击这里
CustomJS 回调
为了让小工具更有用, 我们需要让它能执行某些真是的动作。
下面举例看看CustomJS
如何通过JavaSript编码实现行为的相应。
from bokeh.models import TapTool, CustomJS, ColumnDataSource
callback = CustomJS(code="alert('you tapped a circle!')")
tap = TapTool(callback=callback)
p = figure(plot_width=600, plot_height=300, tools=[tap])
p.circle(x=[1, 2, 3, 4, 5], y=[2, 5, 8, 2, 7], size=20)
show(p)
如上代码可以实现点击某个标记符号时, 将会出现弹窗信息, 并选中该标记点。
控制条的样例
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider
x = [x*0.005 for x in range(0, 201)]
source = ColumnDataSource(data=dict(x=x, y=x))
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
slider = Slider(start=0.1, end=6, value=1, step=.1, title="power")
update_curve = CustomJS(args=dict(source=source, slider=slider), code="""
var data = source.data;
var f = slider.value;
var x = data['x']
var y = data['y']
for (var i = 0; i < x.length; i++) {
y[i] = Math.pow(x[i], f)
}
// necessary becasue we mutated source.data in-place
source.change.emit();
""")
slider.js_on_change('value', update_curve)
show(column(slider, plot))
通过拉动上面的控制条, 可以改变图片中线条的曲线。
数据选择样例
from random import random
x = [random() for x in range(500)]
y = [random() for y in range(500)]
color = ["navy"] * len(x)
s1 = ColumnDataSource(data=dict(x=x, y=y, color=color))
p = figure(plot_width=400, plot_height=400, tools="lasso_select", title="Select Here")
p.circle('x', 'y', color='color', size=8, alpha=0.4, source=s1,
selection_color="firebrick", selection_alpha=0.4)
s2 = ColumnDataSource(data=dict(xm=[0,1],ym=[0.5, 0.5]))
p.line(x='xm', y='ym', color="orange", line_width=5, alpha=0.6, source=s2)
callback = CustomJS(args=dict(s1=s1, s2=s2), code="""
var inds = s1.selected.indices;
if (inds.length == 0)
return;
var ym = 0
for (var i = 0; i < inds.length; i++) {
ym += s1.data.y[inds[i]]
}
ym /= inds.length
s2.data.ym = [ym, ym]
// necessary becasue we mutated source.data in-place
s2.change.emit();
""")
s1.selected.js_on_change('indices', callback)
show(p)
CustomJS for UI Events
更多详细的UI Events 点击bokeh.events
from bokeh.plotting import figure
from bokeh import events
from bokeh.models import CustomJS, Div, Button
from bokeh.layouts import column, row
import numpy as np
x = np.random.random(size=2000) * 100
y = np.random.random(size=2000) * 100
p = figure(tools="box_select")
p.scatter(x, y, radius=1, fill_alpha=0.6, line_color=None)
div = Div(width=400)
button = Button(label="Button", width=300)
layout = column(button, row(p, div))
# Events with no attributes
button.js_on_event(events.ButtonClick, CustomJS(args=dict(div=div), code="""
div.text = "Button!";
"""))
p.js_on_event(events.SelectionGeometry, CustomJS(args=dict(div=div), code="""
div.text = "Selection! <p> <p>" + JSON.stringify(cb_obj.geometry, undefined, 2);
"""))
show(layout)
更多信息
其实有很多interactions和events可以与CustomJS
的回调相连接。
- Widgets - Button, Toggle, Dropdown, TextInput, AutocompleteInput, Select, Multiselect, Slider, (DateRangeSlider), DatePicker,
- Tools - TapTool, BoxSelectTool, HoverTool,
- Selection - ColumnDataSource, AjaxDataSource, BlazeDataSource, ServerDataSource
- Ranges - Range1d, DataRange1d, FactorRange
更多详细信息请见 JavaScript Interactions