Bokeh-视图属性

# 导入库
from bokeh.plotting import figure, output_notebook, show
output_notebook()  # 在notebook中显示

主题

from bokeh.themes import built_in_themes
from bokeh.io import curdoc
x = [1, 2, 3, 4, 5]
y = [6, 7, 6, 4, 5]
curdoc().theme = 'caliber'  # 'caliber'、'dark_minimal'、'light_minimal'
p = figure(title='caliber', plot_width=300, plot_height=300)
p.line(x, y)
show(p)
curdoc().theme = 'dark_minimal'
p = figure(title='dark_minimal', plot_width=300, plot_height=300)
p.line(x, y)
show(p)
curdoc().theme = 'light_minimal'
p = figure(title='light_minimal', plot_width=300, plot_height=300)
p.line(x, y)
show(p)

图表配色

HEX色码

import numpy as np
# 数据
N = 4000
x = np.random.random(size=N) * 100
y = np.random.random(size=N) * 100
radii = np.random.random(size=N) * 1.5
colors = [
    "#%02x%02x%02x" % (int(r), int(g), 150) for r, g in zip(50+2*x, 30+2*y)
]
# 工具条
TOOLS="hover,crosshair,pan,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"
# 画布
p = figure(tools=TOOLS)
# 绘图,散点图
p.scatter(x, y, radius=radii,
          fill_color=colors, fill_alpha=0.6,
          line_color=None)
# 显示
show(p) 

CSS3色名

from math import pi
import pandas as pd
from bokeh.models import (
    Plot, ColumnDataSource, FactorRange, CategoricalAxis, TapTool, HoverTool, OpenURL, CategoricalScale)
from bokeh.models.glyphs import Rect
from bokeh.colors import groups  # CSS3颜色
from bokeh.document import Document
from bokeh.embed import file_html
from bokeh.resources import INLINE
from bokeh.util.browser import view
# 转换bokeh.colors groups数据格式
data = []
for name in groups.__all__:
    group = getattr(groups, name)
    data.extend([(str(x), x.to_hex(), group.__name__) for x in group])
# 使用pandas加载数据
css3_colors = pd.DataFrame(data, columns=["Name", "Color", "Group"])
# 转换数据为bokeh格式
source = ColumnDataSource(dict(
    names  = list(css3_colors.Name),
    groups = list(css3_colors.Group),
    colors = list(css3_colors.Color),
))
# x,y轴的数据范围
xdr = FactorRange(factors=list(css3_colors.Group.unique()))
ydr = FactorRange(factors=list(reversed(css3_colors.Name)))
x_scale, y_scale = CategoricalScale(), CategoricalScale()
# 画布
plot = Plot(x_range=xdr, y_range=ydr, x_scale=x_scale, y_scale=y_scale, plot_width=600, plot_height=2000)
plot.title.text = "CSS3 Color Names" # 标题
# 矩形
rect = Rect(x="groups", y="names", width=1, height=1, fill_color="colors", line_color=None)
rect_renderer = plot.add_glyph(source, rect)
# x轴相关参数
xaxis_above = CategoricalAxis(major_label_orientation=pi/4)
plot.add_layout(xaxis_above, 'above')
xaxis_below = CategoricalAxis(major_label_orientation=pi/4)
plot.add_layout(xaxis_below, 'below')
# y轴参数
plot.add_layout(CategoricalAxis(), 'left')
# 提示工具
url = "http://www.colors.commutercreative.com/@names/"
tooltips = """Click the color to go to:<br /><a href="{url}">{url}</a>""".format(url=url)
# 工具条
tap = TapTool(renderers=[rect_renderer], callback=OpenURL(url=url))
hover = HoverTool(renderers=[rect_renderer], tooltips=tooltips)
plot.tools.extend([tap, hover])
# 显示
show(plot)

ColorBrewer配色

import numpy as np
import pandas as pd
from bokeh.palettes import brewer  # color brewer配色方案
# 数据
N = 20
cats = 10
df = pd.DataFrame(np.random.randint(10, 100, size=(N, cats))).add_prefix('y')
# 堆叠图面积图
def stacked(df):
    df_top = df.cumsum(axis=1)
    df_bottom = df_top.shift(axis=1).fillna({'y0': 0})[::-1]
    df_stack = pd.concat([df_bottom, df_top], ignore_index=True)
    return df_stack
areas = stacked(df)  # 堆叠数据
# 配色方案
colors = brewer['Spectral'][areas.shape[1]]
x2 = np.hstack((df.index[::-1], df.index))
# 画布
p = figure(x_range=(0, N-1), y_range=(0, 800))
p.grid.minor_grid_line_color = '#eeeeee'
# 绘图
p.patches([x2] * areas.shape[1], [areas[c].values for c in areas],
          color=colors, alpha=0.8, line_color=None)
# 显示
show(p)
print(brewer.keys())
dict_keys(['YlGn', 'YlGnBu', 'GnBu', 'BuGn', 'PuBuGn', 'PuBu', 'BuPu', 'RdPu', 'PuRd', 'OrRd', 'YlOrRd', 'YlOrBr', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds', 'Greys', 'PuOr', 'BrBG', 'PRGn', 'PiYG', 'RdBu', 'RdGy', 'RdYlBu', 'Spectral', 'RdYlGn', 'Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3'])

色彩映射

from bokeh.models import ColumnDataSource, ColorBar
from bokeh.palettes import Spectral6
from bokeh.transform import linear_cmap
# 数据
x = [1,2,3,4,5,7,8,9,10]
y = [1,2,3,4,5,7,8,9,10]
# 使用线性映射器
mapper = linear_cmap(field_name='y', palette=Spectral6 ,low=min(y) ,high=max(y))
source = ColumnDataSource(dict(x=x,y=y))  # 生成Bokeh专属数据列,其与pandas中DataFrame类似
# 画布(0号图层)
p = figure(plot_width=300, plot_height=300, title="Linear Color Map Based on Y")
# 画图
p.circle(x='x', y='y', line_color=mapper,color=mapper, fill_alpha=1, size=12, source=source)
# 增加一个有颜色对比条
color_bar = ColorBar(color_mapper=mapper['transform'], width=8,  location=(0,0))
# 将对比条图层添加到画布上
p.add_layout(color_bar, 'right')
# 显示
show(p)
import numpy as np
from bokeh.layouts import gridplot
from bokeh.models import (
    ColumnDataSource,
    ColorBar,
    LinearColorMapper,
    LogColorMapper,
)
from bokeh.palettes import Viridis3, Viridis256  # 调色板
# 数据
x = np.random.random(2500) * 140 - 20
y = np.random.normal(size=2500) * 2 + 5
source = ColumnDataSource(dict(x=x, y=y))
opts = dict(x='x', line_color=None, source=source)
# 绘图函数
def make_plot(mapper, title):
    mapper.low_color = 'blue'
    mapper.high_color = 'red'
    p = figure(toolbar_location=None, tools='', title=title)
    color_bar = ColorBar(color_mapper=mapper, location=(0, 0))
    p.circle(
        x='x', y='y',
        fill_color={'field': 'x', 'transform': mapper}, line_color=None,
        source=source
    )
    p.add_layout(color_bar, 'right')
    return p
# 绘图
p1 = make_plot(LinearColorMapper(palette=Viridis256, low=0, high=100), title='Viridis256 - Linear, low/high = blue/red')
p2 = make_plot(LogColorMapper(palette=Viridis256, low=0, high=100), title='Viridis256 - Log, low/high = blue/red')
p3 = make_plot(LinearColorMapper(palette=Viridis3, low=0, high=100), title='Viridis3 - Linear, low/high = blue/red')
p4 = make_plot(LogColorMapper(palette=Viridis3, low=0, high=100), title='Viridis3 - Log, low/high =, blue/red')
# 显示
show(gridplot([p1, p2, p3, p4], ncols=2, plot_width=400, plot_height=300, toolbar_location=None))

视图属性

线条

import numpy as np
from bokeh.layouts import row, column
from bokeh.models import Slider, Dropdown, CustomJS
# 3中不同的绘制方式
p1 = figure(title="Canvas", plot_width=400, plot_height= 400, output_backend="canvas")
p2 = figure(title="SVG", plot_width=400, plot_height= 400, output_backend="svg")
p3 = figure(title="WebGL", plot_width=400, plot_height= 400, output_backend="webgl")
ys = 10  # Y轴缩放,以增加各向异性
lines = []
for p in (p1, p2, p3):
    t = np.linspace(0, 2 * np.pi, 50)
    x = np.sin(t) * 10
    y = np.cos(t) * 10
    l1 = p.line(x, y * ys, color="#2222aa",
                line_width=6, line_cap='butt',
                line_join='round', line_dash=(10, 6, 3, 6, 3, 6))

    t = np.arange(10)
    t = np.linspace(0, 4 * np.pi, 150)
    x = t - 5
    y = (t + 1) * ((t % 2) * 2 - 1)
    y = np.sin(t) + 5
    l2 = p.line(x, y * ys, color="#22aa22",
                line_width=6, line_cap='butt', line_join='round')

    t = np.arange(10)
    x = t - 5
    y = 0.3 * (t + 1) * ((t % 2) * 2 - 1) - 6
    l3 = p.line(x, y * ys, color="#aa2222",
                line_width=6, line_cap='butt',
                line_join='round', line_dash=(10, 10))
    l4 = p.line(y, x * ys, color="#aa2222",
                line_width=6, line_cap='butt',
                line_join='round', line_dash=(10, 10))

    lines.extend([l1, l2, l3, l4])
# 回调
def add_callback(widget, prop):
    widget.callback = CustomJS(args=dict(widget=widget, lines=lines, prop=prop), code="""
        for (var i = 0; i < lines.length; i++) {
            var glyph = lines[i].glyph;
            glyph[prop] = widget.value;
        }
    """)
# 滑块
def make_slider(prop, start, end, value):
    slider = Slider(title=prop, start=start, end=end, value=value)
    add_callback(slider, prop)
    return slider
# 下拉菜单
def make_dropdown(prop, menu):
    dropdown = Dropdown(label=prop, menu=menu)
    add_callback(dropdown, prop)
    return dropdown
sliders = [
    make_slider('line_width', start=0.2, end=16, value=5),
    make_slider('line_dash_offset', start=0, end=100, value=1),
    make_dropdown('line_cap', [("butt", "butt"), ("round", "round"), ("square", "square")]),
    make_dropdown('line_join', [("miter", "miter"), ("round", "round"), ("bevel", "bevel")]),
]
# sliders = column(*sliders)
# 显示
show(row(column(sliders), column(p1, p2, p3)))

填充

from itertools import product
from math import pi
# 分类轴
cats = ['None', 'Alpha', 'RGB', 'RGBA', 'Alpha+RGB', 'Alpha+RGBA']
# 画布
p = figure(x_range=cats, y_range=cats,
           title="Fill and Line Color Combinations")
# 其他
p.xaxis.axis_label = "Fill Options"
p.xaxis.major_label_orientation = pi/4
p.yaxis.axis_label = "Line Options"
p.grid.grid_line_color = None
p.toolbar_location = None
alpha = 0.5
fill_color = (242, 44, 64)
fill_color_alpha = (242, 44, 64, alpha)
line_color = (0, 0, 0)
line_color_alpha = (0, 0, 0, alpha)
# 定义填充和线的颜色、透明度
fill = [(0, {}),
        (1, {'fill_alpha': alpha}),
        (2, {'fill_color': fill_color}),
        (3, {'fill_color': fill_color_alpha}),
        (4, {'fill_alpha': alpha, 'fill_color': fill_color}),
        (5, {'fill_alpha': alpha, 'fill_color': fill_color_alpha})]
line = [(0, {}),
        (1, {'line_alpha': alpha}),
        (2, {'line_color': line_color}),
        (3, {'line_color': line_color_alpha}),
        (4, {'line_alpha': alpha, 'line_color': line_color}),
        (5, {'line_alpha': alpha, 'line_color': line_color_alpha})]

# 绘图
combinations = product(fill, line)
for comb in combinations:
    x, fill_options = comb[0]
    y, line_options = comb[1]
    options = fill_options.copy()
    options.update(line_options)
    p.circle([cats[x]], [cats[y]], line_width=7, size=50, **options)
# 显示
show(p)

文字

见纸质书籍

标题

p = figure(title="Basic Title", plot_width=300, plot_height=300)
p.circle([1,2], [3,4])
# output_file("title.html")
show(p)
p = figure(title="Left Title", title_location="left",
           plot_width=300, plot_height=300)
p.circle([1,2], [3,4])
# output_file("title.html")
show(p)
p = figure(plot_width=300, plot_height=300)
p.circle([1,2], [3,4])
# 定义标题其他属性
p.title.text = "Title With Options"
p.title.align = "right"
p.title.text_color = "orange"
p.title.text_font_size = "25px"
p.title.background_fill_color = "#aaaaee"
# output_file("title.html")
show(p)
from bokeh.models import Title
# from bokeh.plotting import figure, show, output_file
p = figure(title="Left Title", title_location="left",
           plot_width=300, plot_height=300)
p.circle([1,2], [3,4])
# 增加一个标题
p.add_layout(Title(text="Bottom Centered Title", align="center"), "below")  # 多标题
show(p)
p = figure(title="Top Title with Toolbar", toolbar_location="above",
           plot_width=600, plot_height=300)  # toolbar_location
p.circle([1,2], [3,4])
show(p)
from bokeh.io import push_notebook
plot = figure()
plot.circle([1,2,3], [4,6,5])
handle = show(plot, notebook_handle=True)
# 通过画布的句柄更新数据
plot.title.text = "New Title"
push_notebook(handle=handle)
show(plot)
from bokeh.models import Plot, Title, Circle, ColumnDataSource, LinearAxis, Range1d  
# 画布  
p = Plot(  
    title=None, toolbar_location=None,  
    plot_width=800, plot_height=800,  
    min_border=30,  
    background_fill_color="#F0F0F0",  
    border_fill_color="lightgray")  
# 增加额外的x,y坐标轴,设定其范围  
p.extra_x_ranges["x"] = Range1d(0, 100)  
p.extra_y_ranges["y"] = Range1d(0, 100)  
# 数据  
source = ColumnDataSource(dict(x=[1.0, 2.0, 3.0], y=[1.0, 2.0, 3.0]))  
# 顶部增加一个x轴  
p.add_layout(LinearAxis(axis_label="x_label"), "above")  
p.add_layout(LinearAxis(x_range_name="x"), "above")  
# 底部增加一个x轴  
p.add_layout(LinearAxis(axis_label="x_label"), "below")  
p.add_layout(LinearAxis(x_range_name="x"), "below")  
# 左侧增加一个x轴  
p.add_layout(LinearAxis(axis_label="y_label"), "left")  
p.add_layout(LinearAxis(y_range_name="y"), "left")  
# 右侧增加一个x轴  
p.add_layout(LinearAxis(axis_label="y_label"), "right")  
p.add_layout(LinearAxis(y_range_name="y"), "right")  
# 绘图,灰色圆  
gly = Circle(x="x", y="y", size=10)  
p.add_glyph(source, gly)  
# 标题绘制函数  
def add_title(text, loc, bg):  
    lab = Title(text=text, background_fill_color=bg)  
    p.add_layout(lab, loc)  
# 第1组标题,位置:顶部  
add_title("Title A1", "above", "red")  
add_title("Title A2", "above", "green")  
add_title("Title A3", "above", "lightblue")  
add_title("Title A4", "above", "pink")  
# 第2组标题,位置:底部  
add_title("Title B1", "below", "red")  
add_title("Title B2", "below", "green")  
# 第3组标题,位置:左侧  
add_title("Title L1", "left", "red")  
add_title("Title L2", "left", "green")  
# 第4组标题,位置:右侧  
add_title("Title R1", "right", "red")  
add_title("Title R2", "right", "green")  
add_title("Title R3", "right", "lightblue")  
add_title("Title R4", "right", "pink")  
# 显示  
show(p)  

图例

from bokeh.document import Document
from bokeh.embed import file_html
from bokeh.util.browser import view
from bokeh.resources import INLINE
from bokeh.models.glyphs import Circle, Line
from bokeh.models import (ColumnDataSource, Range1d, Plot, LinearAxis, Grid,
    HoverTool, CrosshairTool, TapTool, WheelZoomTool, Legend, LegendItem, CustomJS)

plot = Plot(x_range=Range1d(-10, 10), y_range=Range1d(-10, 10), plot_width=600, plot_height=600, toolbar_sticky=False)

xaxis = LinearAxis()
yaxis = LinearAxis()
xgrid = Grid(dimension=0, ticker=xaxis.ticker)
ygrid = Grid(dimension=1, ticker=yaxis.ticker)

plot.add_layout(xaxis, "below")
plot.add_layout(yaxis, "left")
plot.add_layout(xgrid)
plot.add_layout(ygrid)

def fds(x, y, e, n):
    d = [
        (x+0*e, y+0*e, 1, "red",    "%s00" % n),
        (x+1*e, y+1*e, 1, "blue",   "%s01" % n),
        (x+2*e, y+2*e, 1, "green",  "%s02" % n),
        (x+3*e, y+3*e, 1, "violet", "%s03" % n),
        (x+4*e, y+4*e, 1, "pink",   "%s04" % n),
        (x+5*e, y+5*e, 1, "black",  "%s05" % n),
        (x+6*e, y+6*e, 1, "gray",   "%s06" % n),
        (x+7*e, y+7*e, 1, "olive",  "%s07" % n),
        (x+8*e, y+8*e, 1, "yellow", "%s08" % n),
        (x+9*e, y+9*e, 1, "orange", "%s09" % n),
    ]
    f = lambda i: [ t[i] for t in d ]
    return dict(x=f(0), y=f(1), s=f(2), c=f(3), name=f(4))

ds1 = ColumnDataSource(data=fds(0, 0, 0.1, "c"))
cr1 = plot.add_glyph(ds1, Circle(x="x", y="y", radius="s", fill_color="c", line_color="c"))

ds2 = ColumnDataSource(data=fds(-5, 5, 0.5, "d"))
cr2 = plot.add_glyph(ds2, Circle(x="x", y="y", radius="s", fill_color="c", line_color="c"))
ln2 = plot.add_glyph(ds2, Line(x="x", y="y", line_width=3, line_color="red"))

ds3 = ColumnDataSource(data=fds(5, 5, 0.0, "e"))
cr3 = plot.add_glyph(ds3, Circle(x="x", y="y", radius="s", fill_color="c", line_color="c"))

tooltips = "<b>@name</b> = (@x{0.00}, @y{0.00})"

hover = HoverTool(tooltips=tooltips, renderers=[cr1, cr2, ln2, cr3], point_policy="follow_mouse")
plot.add_tools(hover)

crosshair = CrosshairTool()
plot.add_tools(crosshair)

tap = TapTool(renderers=[cr1, cr2, cr3], callback=CustomJS(code="console.log('TAP')"))
plot.add_tools(tap)

wheelzoom = WheelZoomTool()
plot.add_tools(wheelzoom)

legends = lambda: [
    LegendItem(label="CR1", renderers=[cr1]),
    LegendItem(label="CR2", renderers=[cr2, ln2]),
    LegendItem(label="CR3", renderers=[cr3]),
]
legend = lambda **kwargs: Legend(background_fill_alpha=0.7, items=legends(), click_policy="hide", **kwargs)

plot.add_layout(legend(location="center_left", orientation="vertical"))
plot.add_layout(legend(location="center", orientation="vertical"))
plot.add_layout(legend(location="top_center", orientation="horizontal"))
plot.add_layout(legend(location="top_right", orientation="horizontal"))
plot.add_layout(legend(location="bottom_right", orientation="horizontal"))
plot.add_layout(legend(location=(0, 0), orientation="vertical", name="(0, 0)"))
plot.add_layout(legend(location="center", orientation="horizontal", name="above"), 'above')
plot.add_layout(legend(location="center", orientation="horizontal", name="below"), 'below')
plot.add_layout(legend(location="center", orientation="vertical", name="left"), 'left')
plot.add_layout(legend(location="center", orientation="vertical", name="right"), 'right')

show(plot)
import numpy as np
from bokeh.plotting import figure, show, output_file
# 数据
x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)
# 画布
p = figure()
# 绘图1,图例
p.circle(x, y, legend="sin(x)")
p.line(x, y, legend="sin(x)")
# 绘图2,图例
p.line(x, 2*y, legend="2*sin(x)",
       line_dash=[4, 4], line_color="orange", line_width=2)
# 绘图3,图例
p.square(x, 3*y, legend="3*sin(x)", fill_color=None, line_color="green")
p.line(x, 3*y, legend="3*sin(x)", line_color="green")
# 图例位置
p.legend.location = "bottom_left"
# 显示
show(p)
import pandas as pd
from bokeh.palettes import Spectral4
from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG
p = figure(plot_width=800, plot_height=250, x_axis_type='datetime')
p.title.text = 'Click on legend entries to hide lines'
for data, name, color in zip([AAPL, IBM, MSFT, GOOG], ["AAPL", "IBM", "MSFT", "GOOG"], Spectral4):
    df = pd.DataFrame(data)
    df['date'] = pd.to_datetime(df['date'])
    p.line(df['date'], df['close'], line_width=2, color=color, alpha=0.8, legend=name)
# 图例位置及点击隐藏效果
p.legend.location = 'top_left'
p.legend.click_policy = 'hide'
show(p)
import numpy as np
from bokeh.models import Legend, LegendItem
from bokeh.sampledata.stocks import AAPL, MSFT
# 时间序列转换
def datetime(x):
    return np.array(x, dtype=np.datetime64)
# 画布
p = figure(background_fill_color="#fafafa", x_axis_type="datetime",
           plot_width=800, plot_height=350)
# 绘图
r = p.multi_line([datetime(AAPL['date']), datetime(MSFT['date'])],
                 [AAPL['adj_close'], MSFT['adj_close']],
                 color=["navy", "crimson"], line_width=2, alpha=0.6)
# 图例
legend = Legend(items=[
    LegendItem(label="AAPL", renderers=[r], index=0),
    LegendItem(label="MSFT", renderers=[r], index=1),
], location="top_left")
p.add_layout(legend)
# 显示
show(p)

色值条

from bokeh.sampledata.autompg import autompg
from bokeh.models import LinearColorMapper, ColorBar
from bokeh.transform import transform
# 数据
source = ColumnDataSource(autompg)
# 色值映射
color_mapper = LinearColorMapper(palette="Viridis256", low=autompg.weight.min(), high=autompg.weight.max())
# 画布
p = figure(x_axis_label='Horsepower', y_axis_label='MPG', tools='save', 
#            toolbar_location=None
          )
# 绘图
p.circle(x='hp', y='mpg', color=transform('weight', color_mapper), size=20, alpha=0.6, source=autompg)
# 色值条
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=12, location=(0,0), title='Weight')
# 添加图层
p.add_layout(color_bar, 'right')
# 显示
show(p)

坐标轴

from bokeh.plotting import figure, output_file, show
# output_file("axes.html")
p = figure(plot_width=400, plot_height=400)
p.circle([1,2,3,4,5], [2,5,8,2,7], size=10)
# 更改x轴设置
p.xaxis.axis_label = "Temp"
p.xaxis.axis_line_width = 3
p.xaxis.axis_line_color = "red"
# 更改y轴设置
p.yaxis.axis_label = "Pressure"
p.yaxis.major_label_text_color = "orange"
p.yaxis.major_label_orientation = "vertical"
# 刻度偏移坐标轴
p.axis.minor_tick_in = -3
p.axis.minor_tick_out = 6
show(p)
import numpy as np
# 数据
x = np.linspace(-6, 6, 500)
y = 8*np.sin(x)*np.sinc(x)
# 画布
p = figure(plot_width=800, plot_height=300, title="", 
#            tools="",toolbar_location=None,
           match_aspect=True)
# 绘图
p.line(x, y, color="navy", alpha=0.4, line_width=4)
p.background_fill_color = "#efefef"
# 其他
p.xaxis.fixed_location = 0
p.yaxis.fixed_location = 0
# 显示
show(p)
# 分类轴
# https://bokeh.pydata.org/en/0.10.0/docs/user_guide/plotting.html
factors = ["a", "b", "c", "d", "e", "f", "g", "h"]
x = [50, 40, 65, 10, 25, 37, 80, 60]
p = figure(y_range=factors)
p.circle(x, factors, size=15, fill_color="orange", line_color="green", line_width=3)
show(p)
# 时间序列
from bokeh.sampledata.stocks import AAPL
# create a new plot with a datetime axis type
p = figure(width=800, height=250, x_axis_type="datetime")
p.line(AAPL['date'], AAPL['adj_close'], color='navy', alpha=0.5)
show(p)
# 指数轴
x = [0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
y = [10**xx for xx in x]
# create a new plot with a log axis type
p = figure(plot_width=400, plot_height=400,
           y_axis_type="log", y_range=(10**-1, 10**4))

p.line(x, y, line_width=2)
p.circle(x, y, fill_color="white", size=8)

show(p)
from numpy import pi, arange, sin, linspace
from bokeh.models import LinearAxis, Range1d
# 数据
x = arange(-2*pi, 2*pi, 0.2)
y = sin(x)
y2 = linspace(0, 100, len(x))
# 画布
p = figure(x_range=(-6.5, 6.5), y_range=(-1.1, 1.1), min_border=80)
p.background_fill_color = "#fafafa"
# 坐标轴1
p.circle(x, y, color="crimson", size=8)
p.yaxis.axis_label = "red circles"
p.yaxis.axis_label_text_color ="crimson"
# 坐标轴2
p.extra_y_ranges['foo'] = Range1d(0, 100)
p.circle(x, y2, color="navy", size=8, y_range_name="foo")
ax2 = LinearAxis(y_range_name="foo", axis_label="blue circles")
ax2.axis_label_text_color ="navy"
p.add_layout(ax2, 'left')
# 显示
show(p)
p = figure(plot_width=400, plot_height=400)
p.circle([1,2,3,4,5], [2,5,8,2,7], size=10)
# 隐藏垂直于x轴的网格
p.xgrid.grid_line_color = None
# 垂直于y轴的网格属性
p.ygrid.grid_line_alpha = 0.5
p.ygrid.grid_line_dash = [6, 4]  # 点划线
# 显示
show(p)
p = figure(plot_width=500, plot_height=200, tools='')
visible_line = p.line([1, 2, 3], [1, 2, 1], line_color="blue")
invisible_line = p.line([1, 2, 3], [2, 1, 2], line_color="pink")
invisible_line.visible = False  # 隐藏粉色线
p.xaxis.visible = False  # 隐藏x轴
p.xgrid.visible = False  # 隐藏x网格
show(p)

WebGL

import numpy as np
# 数据
N = 10000
x = np.random.normal(0, np.pi, N)
y = np.sin(x) + np.random.normal(0, 0.2, N)
# 工具条
TOOLS = "pan,wheel_zoom,box_zoom,reset,save,box_select"
# 画布,webGL
p = figure(tools=TOOLS, output_backend="webgl")
# 绘图
p.circle(x, y, alpha=0.1, nonselection_alpha=0.001)
# 显示
show(p)

绘图设置

工具条

from bokeh.models import BoxSelectTool
# 工具条
tools = "pan,wheel_zoom,box_zoom,reset,save,box_select"
p = figure(plot_width=400, plot_height=400, 
           tools=tools, title=None, 
           toolbar_location="below")
p.circle([1, 2, 3, 4, 5], [2, 5, 8, 2, 7], size=10)
# 数据选框
p.add_tools(BoxSelectTool(dimensions='height'))
show(p)
plot = figure(tools="pan,lasso_select,box_select",   
              plot_width=400, plot_height=400,   
              active_drag="lasso_select")  
plot.circle([1, 2, 3, 4, 5], [2, 5, 8, 2, 7], size=10)  
show(plot)  
import numpy as np
from bokeh.layouts import row
# 数据
N = 1000
x = np.random.random(size=N) * 100
y = np.random.random(size=N) * 100
radii = np.random.random(size=N) * 1.5
colors = ["#%02x%02x%02x" % (int(r), int(g), 150) for r, g in zip(50+2*x, 30+2*y)]
# 绘图函数
def make_plot(autohide=None):
    p = figure(width=300, height=300, title='Autohiding toolbar' if autohide else 'Not autohiding toolbar')
    p.scatter(x, y, radius=radii, fill_color=colors, fill_alpha=0.6, line_color=None)
    p.toolbar.autohide = autohide
    return p
# 显示
show(row(make_plot(True), make_plot(False)))
import numpy as np
from bokeh.layouts import row, column
# 数据
N = 1000
x = np.random.random(size=N) * 100
y = np.random.random(size=N) * 100
radii = np.random.random(size=N) * 1.5
colors = ["#%02x%02x%02x" % (int(r), int(g), 150) for r, g in zip(50+2*x, 30+2*y)]
# 工具条
TOOLS="hover,crosshair,pan,reset,box_select"
# 绘图函数
def mkplot(toolbar, xaxis, yaxis):
    p = figure(width=300, height=300, tools=TOOLS, toolbar_location=toolbar, x_axis_location=xaxis, y_axis_location=yaxis)
    p.scatter(x, y, radius=radii, fill_color=colors, fill_alpha=0.6, line_color=None)
    return p
# 绘图
p_lbl = mkplot(toolbar="left",  xaxis="below", yaxis="left")
p_lbr = mkplot(toolbar="left",  xaxis="below", yaxis="right")
p_rbl = mkplot(toolbar="right", xaxis="below", yaxis="left")
p_rbr = mkplot(toolbar="right", xaxis="below", yaxis="right")

p_lal = mkplot(toolbar="left",  xaxis="above", yaxis="left")
p_lar = mkplot(toolbar="left",  xaxis="above", yaxis="right")
p_ral = mkplot(toolbar="right", xaxis="above", yaxis="left")
p_rar = mkplot(toolbar="right", xaxis="above", yaxis="right")

p_abl = mkplot(toolbar="above", xaxis="below", yaxis="left")
p_aal = mkplot(toolbar="above", xaxis="above", yaxis="left")
p_bbl = mkplot(toolbar="below", xaxis="below", yaxis="left")
p_bal = mkplot(toolbar="below", xaxis="above", yaxis="left")

p_abr = mkplot(toolbar="above", xaxis="below", yaxis="right")
p_aar = mkplot(toolbar="above", xaxis="above", yaxis="right")
p_bbr = mkplot(toolbar="below", xaxis="below", yaxis="right")
p_bar = mkplot(toolbar="below", xaxis="above", yaxis="right")
# 显示布局
layout = column(
    row(p_lbl, p_lbr, p_lal, p_lar),
    row(p_rbl, p_rbr, p_ral, p_rar),
    row(p_abl, p_aal, p_abr, p_aar),
    row(p_bbl, p_bal, p_bbr, p_bar),
)
# 显示
show(layout)
import numpy as np
from bokeh.layouts import row, column, gridplot
# 数据
N = 1000
x = np.random.random(size=N) * 100
y = np.random.random(size=N) * 100
radii = np.random.random(size=N) * 1.5
colors = ["#%02x%02x%02x" % (int(r), int(g), 150) for r, g in zip(50+2*x, 30+2*y)]
# 工具条
TOOLS="hover,crosshair,pan,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select"
# 绘图
def mkplot(xaxis="below", yaxis="left"):
    p = figure(width=300, height=300, tools=TOOLS, x_axis_location=xaxis, y_axis_location=yaxis)
    p.scatter(x, y, radius=radii, fill_color=colors, fill_alpha=0.6, line_color=None)
    return p
def mkgrid(plots, location):
    return gridplot(plots, plot_width=300, plot_height=300, toolbar_location=location)
l_al = mkgrid([[mkplot(), mkplot()], [mkplot(), mkplot()]], "above")
l_ar = mkgrid([[mkplot(), mkplot()], [mkplot(), mkplot()]], "below")
l_bl = mkgrid([[mkplot(), mkplot()], [mkplot(), mkplot()]], "left")
l_br = mkgrid([[mkplot(), mkplot()], [mkplot(), mkplot()]], "right")
layout = column(row(l_al, l_ar), row(l_bl, l_br))
# 显示
show(layout)

上下界标记

import numpy as np
from bokeh.models.annotations import Span
# 数据
x = np.linspace(0, 20, 200)
y = np.sin(x)
p = figure(y_range=(-2, 2))
p.line(x, y)
# 边界标记,上限
upper = Span(location=1, dimension='width', line_color='olive', line_width=4)
p.add_layout(upper)
# 边界标记,下线
lower = Span(location=-1, dimension='width', line_color='firebrick', line_width=4)
p.add_layout(lower)
# 显示
show(p)

箱型区域标记

import numpy as np
from bokeh.models.annotations import BoxAnnotation
x = np.linspace(0, 20, 200)
y = np.sin(x)
p = figure(y_range=(-2, 2))
# 正弦曲线
p.line(x, y)
# 填充上限区域
upper = BoxAnnotation(bottom=1, fill_alpha=0.1, fill_color='olive')
p.add_layout(upper)
# 填充下限区域
lower = BoxAnnotation(top=-1, fill_alpha=0.1, fill_color='firebrick')
p.add_layout(lower)
# 填充有限区域
center = BoxAnnotation(top=0.6, bottom=-0.3, left=7, right=12, fill_alpha=0.1, fill_color='navy')
p.add_layout(center)
# 显示
show(p)

选择范围

import numpy as np
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, RangeTool
from bokeh.sampledata.stocks import AAPL
# 数据
dates = np.array(AAPL['date'], dtype=np.datetime64)
source = ColumnDataSource(data=dict(date=dates, close=AAPL['adj_close']))
# 画布
p = figure(plot_height=300, plot_width=800, tools="xpan", toolbar_location=None,
           x_axis_type="datetime", x_axis_location="above",
           background_fill_color="#efefef", x_range=(dates[1500], dates[2500]))
# 绘图
p.line('date', 'close', source=source)
p.yaxis.axis_label = 'Price'
# 绘图2
select = figure(title="Drag the middle and edges of the selection box to change the range above",
                plot_height=130, plot_width=800, y_range=p.y_range,
                x_axis_type="datetime", y_axis_type=None,
                tools="", toolbar_location=None, background_fill_color="#efefef")
# 范围工具
range_tool = RangeTool(x_range=p.x_range)
range_tool.overlay.fill_color = "navy"
range_tool.overlay.fill_alpha = 0.2
# 绘图
select.line('date', 'close', source=source)
select.ygrid.grid_line_color = None
select.add_tools(range_tool)
select.toolbar.active_multi = range_tool
# 显示
show(column(p, select))

标签

from bokeh.models.annotations import Label
from bokeh.plotting import figure
p = figure(x_range=(0,10), y_range=(0,10))
p.circle([2, 5, 8], [4, 7, 6], color="olive", size=10)
# 标签作为一个独立图层,设定其位坐标、偏移、文字、基线等
label = Label(x=5, y=7, x_offset=12, text="Second Point", text_baseline="middle")
p.add_layout(label)
show(p)
from bokeh.models import Plot, Range1d, Label, LinearAxis
# 画布
plot = Plot(plot_width=600, plot_height=600,
            x_range=Range1d(0, 10), y_range=Range1d(0, 10),
            toolbar_location=None)
# 标签1
label1 = Label(x=1, y=6, x_offset=25, y_offset=25,
               text="Demo Label",
               text_font_size='38pt', text_color='red', text_alpha=0.9,
               text_baseline='bottom', text_align='left',
               background_fill_color='green', background_fill_alpha=0.2,
               angle=15, angle_units='deg',
               render_mode='canvas')
# 标签2
label2 = Label(x=3, y=5.5, text="(I'm Canvas)", text_font_size='20pt',
               border_line_color='black', border_line_width=2, border_line_dash='8 4',
               render_mode='canvas')
# 标签3
label3 = Label(x=1, y=2, x_offset=25, y_offset=25,
               text="Demo Label",
               text_font_size='38pt', text_color='red', text_alpha=0.9,
               text_baseline='bottom', text_align='left',
               background_fill_color='green', background_fill_alpha=0.2,
               angle=0.261, angle_units='rad',
               render_mode='css')
# 标签4
label4 = Label(x=3, y=1.0, text="(I'm CSS)", text_font_size='20pt',
               border_line_color='black', border_line_width=2, border_line_dash='8 4',
               render_mode='css')
# 顶部
label_above = Label(
    x=0, y=0, text="Label in above panel", x_units='screen', y_units='screen',
    text_font_size='38pt', text_color='firebrick', text_alpha=0.9,
)
# 左侧
label_left = Label(
    x=0, y=0, text="Label in left panel",
    x_units='screen', y_units='screen', angle=90, angle_units='deg',
    text_font_size='18pt', text_color='firebrick', text_alpha=0.9,
    background_fill_color='aliceblue', text_baseline='top',
)
# 自定义属性
plot.add_layout(LinearAxis(), 'below')
plot.add_layout(LinearAxis(), 'left')
plot.add_layout(label1)
plot.add_layout(label2)
plot.add_layout(label3)
plot.add_layout(label4)
plot.add_layout(label_above, 'above')
plot.add_layout(label_left, 'left')
# 显示
show(plot)
WARNING:bokeh.core.validation.check:W-1000 (MISSING_RENDERERS): Plot has no renderers: Plot(id='1313', ...)

标签组

from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, LabelSet
# 数据
source = ColumnDataSource(data=dict(
    temp=[166, 171, 172, 168, 174, 162],
    pressure=[165, 189, 220, 141, 260, 174],
    names=['A', 'B', 'C', 'D', 'E', 'F']))
# 画布
p = figure(x_range=(160, 175))
p.scatter(x='temp', y='pressure', size=8, source=source) #散点图
p.xaxis.axis_label = 'Temperature (C)'
p.yaxis.axis_label = 'Pressure (lbs)'
# 标签组
labels = LabelSet(x='temp', y='pressure', text='names', level='glyph',
                  x_offset=5, y_offset=5, source=source, render_mode='canvas')
p.add_layout(labels)
# 显示
show(p)
from bokeh.models import Plot, Range1d, LabelSet, LinearAxis, ColumnDataSource

source = ColumnDataSource(data=dict(text=['one', 'two', 'three'],
                                    x1=[1,4,7],
                                    x2=[60,240,420]))
# Have to specify x/y range as labels aren't included in the plot area solver
plot = Plot(plot_width=600, plot_height=600,
            x_range=Range1d(0, 10), y_range=Range1d(0, 10),
            toolbar_location=None)

label_set1 = LabelSet(x='x1', y=2, x_offset=25, y_offset=25,
                      text="text", source=source,
                      text_font_size='38pt', text_color='red', text_alpha=0.9,
                      text_baseline='bottom', text_align='left',
                      background_fill_color='green', background_fill_alpha=0.2,
                      angle=15, angle_units='deg',
                      render_mode='canvas')

label_set2 = LabelSet(x='x2', y=4, x_units='screen', x_offset=25, y_offset=25,
                      text="text", source=source,
                      text_font_size='38pt', text_color='red', text_alpha=0.9,
                      text_baseline='bottom', text_align='left',
                      background_fill_color='green', background_fill_alpha=0.2,
                      angle=15, angle_units='deg',
                      render_mode='css')

plot.add_layout(LinearAxis(), 'below')
plot.add_layout(LinearAxis(), 'left')
plot.add_layout(label_set1)
plot.add_layout(label_set2)
show(plot)
WARNING:bokeh.core.validation.check:W-1000 (MISSING_RENDERERS): Plot has no renderers: Plot(id='3961', ...)

箭头

from bokeh.models.annotations import Arrow
from bokeh.models.arrow_heads import OpenHead, NormalHead, VeeHead
# 画布
p = figure(plot_width=600, plot_height=600)
# 画图:3圆
p.circle(x=[0, 1, 0.5], y=[0, 0, 0.7], radius=0.1,
         color=["navy", "yellow", "red"], fill_alpha=0.1)
# 箭头1,定义箭头形式,线宽,起终点表表
p.add_layout(Arrow(end=OpenHead(line_color="firebrick", line_width=4),
                   x_start=0, y_start=0, x_end=1, y_end=0))
# 箭头2
p.add_layout(Arrow(end=NormalHead(fill_color="orange"),
                   x_start=1, y_start=0, x_end=0.5, y_end=0.7))
# 箭头3
p.add_layout(Arrow(end=VeeHead(size=35), line_color="red",
                   x_start=0.5, y_start=0.7, x_end=0, y_end=0))
# 显示
show(p)

提示工具

基本配置

from bokeh.models import ColumnDataSource
# 数据
source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    desc=['A', 'b', 'C', 'd', 'E'],
))
# 提示工具
TOOLTIPS = [
    ("index", "$index"),
    ("(x,y)", "($x, $y)"),
    ("desc", "@desc"),
]
# 画布
p = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,
           title="Mouse over the dots")
# 绘图
p.circle('x', 'y', size=20, source=source)
# 显示
show(p)

格式化提示内容

import numpy as np
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.plotting import figure
from bokeh.sampledata.stocks import AAPL # Sample stock data [bokeh built-in]
# 也可以加载数据后直接用pandas来将其转化为时间序列
def datetime(x):
    return np.array(x, dtype=np.datetime64)
# 数据
source = ColumnDataSource(data={
    'date'      : datetime(AAPL['date'][::10]),
    'adj close' : AAPL['adj_close'][::10],
    'volume'    : AAPL['volume'][::10],
})
# 画布
p = figure(plot_height=250, x_axis_type="datetime",
#            tools="", toolbar_location=None,
           title="Hover Tooltip Formatting", sizing_mode="scale_width")
p.background_fill_color="#f5f5f5"
p.grid.grid_line_color="white"
p.xaxis.axis_label = 'Date'
p.yaxis.axis_label = 'Price'
p.axis.axis_line_color = None
# 绘图,线
p.line(x='date', y='adj close', line_width=2, color='#ebbd5b', source=source)
# 格式化提示内容HoverTool
p.add_tools(HoverTool(
    tooltips=[
        ( 'date',   '@date{%F}'            ),
        ( 'close',  '$@{adj close}{%0.2f}' ), # 使用 @{ }指向引用的数据,并设定其格式
        ( 'volume', '@volume{0.00 a}'      ),
    ],
    formatters={
        'date'      : 'datetime', # 使用'datetime'格式化‘date’数据列
        'adj close' : 'printf',   # 使用'printf'格式化'adj close'数据列
                                  # 使用 'numeral' 格式化其他数据列,如 'volume'
    },

    # 鼠标悬停区域对应垂直于折线的数据
    mode='vline'
))
# 显示
show(p)

自定义HTML提示工具

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    desc=['A', 'b', 'C', 'd', 'E'],
    imgs=[
        'https://bokeh.pydata.org/static/snake.jpg',
        'https://bokeh.pydata.org/static/snake2.png',
        'https://bokeh.pydata.org/static/snake3D.png',
        'https://bokeh.pydata.org/static/snake4_TheRevenge.png',
        'https://bokeh.pydata.org/static/snakebite.jpg'
    ],
    fonts=[
        '<i>italics</i>',
        '<pre>pre</pre>',
        '<b>bold</b>',
        '<small>small</small>',
        '<del>del</del>'
    ]
))
# 直接使用HTML标签
TOOLTIPS = """
    <div>
        <div>
            <img
                src="@imgs" height="42" alt="@imgs" width="42"
                style="float: left; margin: 0px 15px 15px 0px;"
                border="2"
            ></img>
        </div>
        <div>
            <span style="font-size: 17px; font-weight: bold;">@desc</span>
            <span style="font-size: 15px; color: #966;">[$index]</span>
        </div>
        <div>
            <span>@fonts{safe}</span>
        </div>
        <div>
            <span style="font-size: 15px;">Location</span>
            <span style="font-size: 10px; color: #696;">($x, $y)</span>
        </div>
    </div>
"""
# 画布
p = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,
           title="Mouse over the dots")
# 绘图
p.circle('x', 'y', size=20, source=source)
# 显示
show(p)

回调函数

from bokeh.models import ColumnDataSource, CustomJS
x = [2, 3, 5, 6, 8, 7]
y = [6, 4, 3, 8, 7, 5]
p = figure(width=400, height=400, tools="hover", toolbar_location=None, title='Hovering over point:')
cr = p.circle(x, y, color='olive', radius=1, alpha=0.4, hover_color='olive', hover_alpha=1.0)
callback = CustomJS(args={'title': p.title}, code="""
    const indices = cb_data.index.indices;
    title.text = 'Hovering over points: ' + indices
""")
# CustomJS回调hover返回参数对标题进行传参
p.hover.tooltips=None
p.hover.callback=callback
show(p)

用户自定义

import pandas as pd
from bokeh.plotting import figure, show
from bokeh.sampledata.periodic_table import elements
# 数据
elements = elements.copy()
elements = elements[elements.group != "-"]
elements.sort_values('metal', inplace=True)
# 颜色
colormap = {
    "alkali metal"         : "#a6cee3",
    "alkaline earth metal" : "#1f78b4",
    "halogen"              : "#fdbf6f",
    "metal"                : "#b2df8a",
    "metalloid"            : "#33a02c",
    "noble gas"            : "#bbbb88",
    "nonmetal"             : "#baa2a6",
    "transition metal"     : "#e08e79",
}
data=dict(
    atomic_number=elements["atomic number"],
    sym=elements["symbol"],
    name=elements["name"],
    atomic_mass = pd.to_numeric(elements['atomic mass'], errors="coerce"),
    density=elements['density'],
    metal=[x.title() for x in elements["metal"]],
    type_color=[colormap[x] for x in elements["metal"]]
)
mass_format = '{0.00}'
# 工具条
TOOLTIPS = """
    <div style="width: 62px; height: 62px; opacity: .8; padding: 5px; background-color: @type_color;>
    <h1 style="margin: 0; font-size: 12px;"> @atomic_number </h1>
    <h1 style="margin: 0; font-size: 24px;"><strong> @sym </strong></h1>
    <p style=" margin: 0; font-size: 8px;"><strong> @name </strong></p>
    <p style="margin: 0; font-size: 8px;"> @atomic_mass{mass_format} </p>
    </div>
""".format(mass_format=mass_format)
# 画布
p = figure(plot_width=900, plot_height=450, tooltips=TOOLTIPS, tools='save',title='Densities by Atomic Mass')
p.background_fill_color = "#fafafa"
# 绘图
p.circle('atomic_mass', 'density', size=12, source=data, color='type_color',
         line_color="black", legend='metal', alpha=0.9)
# 其他
p.legend.glyph_width = 30
p.legend.glyph_height = 30
p.xaxis.axis_label= 'Atomic Mass'
p.yaxis.axis_label= 'Density'
p.xgrid.grid_line_color = None
# p.toolbar_location = None
l = p.legend[0]
p.add_layout(l, 'right')
l.border_line_color = None
# 显示
show(p)
import itertools
import numpy as np
# 工具条
TOOLS="crosshair,pan,wheel_zoom,box_zoom,reset,hover,save"

TOOLTIPS = [
    ("index", "$index"),
    ("(x,y)", "($x, $y)"),
    ("radius", "@radius"),
    ("fill color", "$color[hex, swatch]:colors"),
    ("foo", "@foo"),
    ("bar", "@bar"),
]
# 数据
xx, yy = np.meshgrid(range(0,101,4), range(0,101,4))
x = xx.flatten()
y = yy.flatten()
N = len(x)
inds = [str(i) for i in np.arange(N)]
radii = np.random.random(size=N)*0.4 + 1.7
colors = [
    "#%02x%02x%02x" % (int(r), int(g), 150) for r, g in zip(50+2*x, 30+2*y)
]
data=dict(
    x=x,
    y=y,
    radius=radii,
    colors=colors,
    foo=list(itertools.permutations("abcdef"))[:N],
    bar=np.random.normal(size=N),
)  # ColumnDataSource
# 画布
p = figure(title="Hoverful Scatter", tools=TOOLS, tooltips=TOOLTIPS)
# 绘图
r = p.circle(x='x', y='y', radius='radius', source=data,
             fill_color='colors', fill_alpha=0.6, line_color=None)
p.hover.renderers = [r] # 仅对圆进行渲染悬停效果
# 文本
p.text(x, y, text=inds, alpha=0.5, text_font_size="5pt",
       text_baseline="middle", text_align="center")
# 显示
show(p)

图形显示布局

行列显示

from bokeh.models import ColumnDataSource
import pandas as pd
# 读取数据
df = pd.read_csv('data/all_stocks_5yr.csv')
df_apple = df[df['Name'] == 'AAL']  # 苹果股价
source = ColumnDataSource(data = {
    'x' : df_apple['high'],
    'y' : df_apple['low'],
    'x1': df_apple['open'],
    'y1': df_apple['close']
})
# 创建画布1,绘图
plot1 = figure(plot_width=250, plot_height=250)
plot1.cross(x = 'high', y = 'low', source = df_apple, color = 'red', size = 10, alpha = 0.8)
# 创建画布2,绘图
plot2 = figure(plot_width=250, plot_height=250)
plot2.circle(x = 'open', y = 'close', source = df_apple, color = 'green', size = 10, alpha = 0.3)

GlyphRenderer(id = '1077', …)data_source = ColumnDataSource(id='1073', ...),glyph = Circle(id='1075', ...),hover_glyph = None,js_event_callbacks = {},js_property_callbacks = {},level = 'glyph',muted = False,muted_glyph = None,name = None,nonselection_glyph = Circle(id='1076', ...),selection_glyph = None,subscribed_events = [],tags = [],view = CDSView(id='1078', ...),visible = True,x_range_name = 'default',y_range_name = 'default')

from bokeh.layouts import row
# 采用行显示
row_layout = row(plot1,plot2)
show(row_layout)
row_layout =row(children=[plot1,plot2], sizing_mode='stretch_both')
show(row_layout)
# 同一张画布
plot = figure()
plot.cross(x = 'x', y = 'y', source = data, color = 'red', size = 10, alpha = 0.8)
plot.circle(x = 'x1', y = 'y1', source = data, color = 'green', size = 10, alpha = 0.3)
show(plot)
from bokeh.layouts import column
# 列显示
col_layout = column(plot1,plot2)
show(col_layout)
# 行列组合显示
nested_layout = column(row(plot1,plot2), row(plot2,plot1))
show(nested_layout)
import bokeh.plotting
import bokeh.layouts
fig1 = bokeh.plotting.figure()
fig1.sizing_mode = 'scale_width'
fig2 = bokeh.plotting.figure()
fig2.sizing_mode = 'scale_width'
column = bokeh.layouts.column([fig1, fig2])
column.sizing_mode = 'scale_width'

网格显示

import numpy as np
from bokeh.layouts import gridplot
from bokeh.palettes import Viridis3
# 数据
N = 100
x = np.linspace(0, 4*np.pi, N)
y0 = np.sin(x)
y1 = np.cos(x)
y2 = np.sin(x) + np.cos(x)
# 画布
p1 = figure(plot_width=250, plot_height=250, title=None)
# 图1
p1.circle(x, y0, size=3, color=Viridis3[0])
# 图2
p2 = figure(plot_width=250, plot_height=250, title=None)
p2.circle(x, y1, size=3, color=Viridis3[1])
# 图3
p3 = figure(plot_width=250, plot_height=250, title=None)
p3.circle(x, y2, size=3, color=Viridis3[2])
# 网格
# grid = gridplot([[p1, p2], [None, p3]]) 或 gridplot([s1, s2, s3], ncols=2)
grid = gridplot([[p1, p2,p3]])
# 显示
show(grid)
grid = gridplot([[p1, p2,p3]],merge_tools=True)
# 显示
show(grid)

选项卡显示

from bokeh.models.widgets import Tabs, Panel  # 面板和选项卡
tab1 = Panel(child = plot1, title = 'Tab One')
tab2 = Panel(child = row(plot2,plot1), title = 'Tab Two')
tab3 = Panel(child = column(row(plot1,plot2), row(plot2,plot1)), title = 'Tab Three')
tabs_object = Tabs(tabs = [tab1, tab2,tab3])
show(tabs_object)

图形输出

x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]
p = figure(title="simple line example", x_axis_label='x', y_axis_label='y')
p.line(x, y, legend="Temp.", line_width=2)
show(p)
from bokeh.io import export_png
export_png(p, filename="plot.png")
'D:\\pybook\\03-DataVisualization\\01-20180514bokeh\\bokeh_book_myself\\plot.png'
p.output_backend = "svg"
from bokeh.io import export_svgs
export_svgs(p, filename="plot.svg")
['plot.svg']
from bokeh.plotting import figure
from bokeh.layouts import row
from bokeh.io import save
def make_figure(output_backend):
    p = figure(plot_width=400,
               plot_height=400,
               output_backend=output_backend,
               title="Backend: %s" % output_backend)

    p.circle(x=[1, 2, 3], y=[1, 2, 3], radius=0.25, color="blue", alpha=0.5)
    p.annulus(x=[1, 2, 3], y=[1, 2, 3], inner_radius=0.1, outer_radius=0.20, color="orange")
    return p
canvas = make_figure("canvas")
webgl  = make_figure("webgl")
svg    = make_figure("svg")

save(row(canvas, webgl, svg))
'C:\\Users\\yeayee\\AppData\\Local\\Temp\\tmpsqkmzk3p.html'

使用工具条进行数据交互

套索关联

from bokeh.layouts import row
from bokeh.models import ColumnDataSource
from bokeh.sampledata.autompg import autompg
from bokeh.transform import jitter
source = ColumnDataSource(autompg)
TOOLS = "save,box_select,lasso_select"  # 套索
s1 = figure(tools=TOOLS, plot_width=400, plot_height=400,
            x_axis_label='# Cylinders', y_axis_label='MPG')
s1.circle(jitter('cyl', 0.5), 'mpg', source=source)
s2 = figure(tools=TOOLS, plot_width=400, plot_height=400,
            x_axis_label='Acceleration', y_axis_label='MPG')
# 通过相同的数据建立连接
s2.circle('accel', 'mpg', source=source)
# 显示
show(row(s1,s2))
# 数据
N = 100
x = np.linspace(0, 4*np.pi, N)
y0 = np.sin(x)
y1 = np.cos(x)
y2 = np.sin(x) + np.cos(x)
# 画布1
s1 = figure(title=None)
s1.circle(x, y0, size=3, color="navy", alpha=0.5)
# 画布2,关联画布1的x,y轴范围
s2 = figure(x_range=s1.x_range, y_range=s1.y_range, title=None)
s2.circle(x, y1, size=3, color="firebrick", alpha=0.5)
# 画布3,关联画布1的x轴范围,仅随着画布1的x轴方向拖动变化
s3 = figure(x_range=s1.x_range, title=None)
s3.circle(x, y2, size=3, color="olive", alpha=0.5)
# 网格
p = gridplot([[s1, s2, s3]], toolbar_location=None, plot_width=250, plot_height=250)
# 显示
show(p)
from bokeh.models import ColumnDataSource
# 数据
N = 300
x = np.linspace(0, 4*np.pi, N)
y0 = np.sin(x)
y1 = np.cos(x)
source = ColumnDataSource(data=dict(x=x, y0=y0, y1=y1))
# 工具条
TOOLS = "pan,wheel_zoom,box_zoom,reset,save,box_select,lasso_select"
# 绘制左图
left = figure(tools=TOOLS, width=350, height=350, title=None)
left.circle('x', 'y0', source=source)
# 绘制右图
right = figure(tools=TOOLS, width=350, height=350, title=None)
right.circle('x', 'y1', source=source)
# 网格显示,用选框或套索选择左图(x范围)数据时,对应显示右图数据
p = gridplot([[left, right]])
show(p)
x = list(range(-20, 21))
y0 = [abs(xx) for xx in x]
y1 = [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, plot_width=300, plot_height=300, title=None)
left.circle('x', 'y0', source=source)

# create another new plot and add a renderer
right = figure(tools=TOOLS, plot_width=300, plot_height=300, title=None)
right.circle('x', 'y1', source=source)

p = gridplot([[left, right]])

show(p)
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter
# 数据
x = list(range(-20, 21))
y0 = [abs(xx) for xx in x]
y1 = [xx**2 for xx in x]
source = ColumnDataSource(data=dict(x=x, y0=y0, y1=y1))
# CDSView创一个过滤器,仅选择部分数据渲染
view = CDSView(source=source, filters=[BooleanFilter([True if y > 250 or y < 100 else False for y in y1])])
# 工具条
TOOLS = "box_select,lasso_select"
# 创建图1,完整显示数据
left = figure(tools=TOOLS, plot_width=300, plot_height=300, title=None)
left.circle('x', 'y0', size=10, hover_color="firebrick", source=source)
# 用CDSView过滤后的数据进行绘制,右图部分数据不可见
right = figure(tools=TOOLS, plot_width=300, plot_height=300, title=None)
right.circle('x', 'y1', size=10, hover_color="firebrick", source=source, view=view)
# 网格显示
p = gridplot([[left, right]])
show(p)

平移关联

from bokeh.layouts import gridplot
import numpy as np
# 数据
N = 100
x = np.linspace(0, 4*np.pi, N)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.sin(x) + np.cos(x)
# 画布1
s1 = figure(plot_width=350, plot_height=350)
# 绘图
s1.circle(x, y1, color="navy", size=8, alpha=0.5)
# 画布2,建立关联数据
s2 = figure(plot_width=350, plot_height=350,
            x_range=s1.x_range, y_range=s1.y_range)
# 绘图
s2.circle(x, y2, color="firebrick", size=8, alpha=0.5)
# 画布3,建立关联数据
s3 = figure(plot_width=350, plot_height=350, x_range=s1.x_range)
# 绘图
s3.circle(x, y3, color="olive", size=8, alpha=0.5)
# 显示布局
p = gridplot([[s1,s2, s3]])
# 显示
show(p)

使用控件进行数据交互

from bokeh.layouts import widgetbox
# 按钮
from bokeh.models.widgets import Button
button_warning = Button(label="Foo Warning", button_type="warning")
button_success = Button(label="Foo Success", button_type="success")
show(widgetbox(button_warning, button_success))
# 复选_按钮组键
from bokeh.models.widgets import CheckboxButtonGroup

checkbox_button_group = CheckboxButtonGroup(
        labels=["Option 1", "Option 2", "Option 3"], 
        active=[1, 2]) # 激活选项
show(widgetbox(checkbox_button_group))
# 复选框
from bokeh.models.widgets import CheckboxGroup
checkbox_group = CheckboxGroup(
        labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
show(widgetbox(checkbox_group))
# 滑动条
from bokeh.models.widgets import Slider, RangeSlider
slider = Slider(start=0, end=10, value=3, step=.1, title="Slider")
range_slider = RangeSlider(start=0, end=10, value=(5,9), step=.1, title="Range Slider")
show(widgetbox(slider, range_slider))
# 选项卡
from bokeh.models.widgets import Panel, Tabs
from bokeh.plotting import figure
p1 = figure(plot_width=300, plot_height=300)
p1.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="red", alpha=0.5)
tab1 = Panel(child=p1, title="circle")
p2 = figure(plot_width=300, plot_height=300)
p2.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=3, color="blue", alpha=0.5)
tab2 = Panel(child=p2, title="line")
tabs = Tabs(tabs=[ tab1, tab2 ])
show(tabs)
from datetime import date

from bokeh.document import Document
from bokeh.embed import file_html
from bokeh.resources import INLINE
from bokeh.util.browser import view
from bokeh.models import ColumnDataSource
from bokeh.models.layouts import Column, Row, Tabs, Panel
from bokeh.models.widgets import (
    Button, Toggle, Dropdown,
    CheckboxGroup, RadioGroup,
    CheckboxButtonGroup, RadioButtonGroup,
    TextInput, AutocompleteInput,
    Select, MultiSelect,
    Slider, RangeSlider, DateSlider, DateRangeSlider,
    Spinner, ColorPicker, DatePicker,
    Paragraph, Div, PreText,
    DataTable, TableColumn,
    StringFormatter, NumberFormatter,
    StringEditor, IntEditor, NumberEditor, SelectEditor,
)
from bokeh.plotting import figure
from bokeh.sampledata.iris import flowers
from bokeh.sampledata.autompg2 import autompg2 as mpg

click_button = Button(label="Button still has click event", button_type="success")

disabled_button = Button(label="Button (disabled) - still has click event", button_type="primary", disabled=True)

toggle = Toggle(label="Toggle button", button_type="success")

menu = [("Item 1", "item_1_value"), ("Item 2", "item_2_value"), None, ("Item 3", "item_3_value")]

dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu)
dropdown_split = Dropdown(label="Split button", button_type="danger", menu=menu, split=True)

checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0)

checkbox_button_group = CheckboxButtonGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
radio_button_group = RadioButtonGroup(labels=["Option 1", "Option 2", "Option 3"], active=0)

text_input = TextInput(placeholder="Enter value ...")

completions = ["aaa", "aab", "aac", "baa", "caa"]
autocomplete_input = AutocompleteInput(placeholder="Enter value (auto-complete) ...", completions=completions)

select = Select(options=["Option 1", "Option 2", "Option 3"])

multi_select = MultiSelect(options=["Option %d" % (i+1) for i in range(16)], size=6)

slider = Slider(value=10, start=0, end=100, step=0.5)

range_slider = RangeSlider(value=[10, 90], start=0, end=100, step=0.5)

date_slider = DateSlider(value=date(2016, 1, 1), start=date(2015, 1, 1), end=date(2017, 12, 31))

date_range_slider = DateRangeSlider(value=(date(2016, 1, 1), date(2016, 12, 31)), start=date(2015, 1, 1), end=date(2017, 12, 31))

spinner = Spinner(value=100)

color_picker = ColorPicker(color="red", title="Choose color:")

date_picker = DatePicker(value=date(2017, 8, 1))

paragraph = Paragraph(text="some text")

div = Div(text="some <b>text</b>")

pre_text = PreText(text="some text")

def mk_tab(color):
    plot = figure(plot_width=300, plot_height=300)
    plot.scatter(flowers["petal_length"], flowers["petal_width"], color=color, fill_alpha=0.2, size=12)
    return Panel(title="Tab 1: %s" % color.capitalize(), child=plot)

tabs = Tabs(tabs=[mk_tab("red"), mk_tab("green"), mk_tab("blue")])

source = ColumnDataSource(data=mpg)
columns = [
    TableColumn(field="manufacturer",
                title="Manufacturer",
                editor=SelectEditor(options=sorted(mpg["manufacturer"].unique())),
                formatter=StringFormatter(font_style="bold")),
    TableColumn(field="model",
                title="Model",
                editor=StringEditor(completions=sorted(mpg["model"].unique()))),
    TableColumn(field="displ",
                title="Displacement",
                editor=NumberEditor(step=0.1),
                formatter=NumberFormatter(format="0.0")),
    TableColumn(field="year",
                title="Year",
                editor=IntEditor()),
    TableColumn(field="cyl",
                title="Cylinders",
                editor=IntEditor()),
    TableColumn(field="trans",
                title="Transmission",
                editor=SelectEditor(options=sorted(mpg["trans"].unique()))),
    TableColumn(field="drv",
                title="Drive",
                editor=SelectEditor(options=sorted(mpg["drv"].unique()))),
    TableColumn(field="class",
                title="Class",
                editor=SelectEditor(options=sorted(mpg["class"].unique()))),
    TableColumn(field="cty",
                title="City MPG",
                editor=IntEditor()),
    TableColumn(field="hwy",
                title="Highway MPG",
                editor=IntEditor()),
]
table = DataTable(source=source, columns=columns, editable=True, width=800)

widgets = Column(children=[
    Row(children=[
        Column(children=[
            click_button, disabled_button, toggle, dropdown, dropdown_split,
            checkbox_group, radio_group,
            checkbox_button_group, radio_button_group,
        ]),
        Column(children=[
            text_input, autocomplete_input,
            select, multi_select,
            slider, range_slider, date_slider, date_range_slider,
            spinner, color_picker, date_picker,
            paragraph, div, pre_text,
        ]),
        tabs,
    ]),
    table,
])

doc = Document()
def fc_doc(doc):
    doc.add_root(widgets)
show(fc_doc)
# 表格
from bokeh.layouts import widgetbox
from datetime import date
from random import randint
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
data = dict(
        dates=[date(2017, 3, i+1) for i in range(10)],
        downloads=[randint(0, 100) for i in range(10)],
    )
source = ColumnDataSource(data)
columns = [
        TableColumn(field="dates", title="Date", formatter=DateFormatter()),
        TableColumn(field="downloads", title="Downloads"),
    ]
data_table = (source=source, columns=columns, width=400, height=280)
# 显示表格,点击表头,自动排序
show(widgetbox(data_table))
# JS回调
from bokeh.layouts import widgetbox
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure
# 数据
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
# 画布
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
# 回调JS函数
callback = CustomJS(args=dict(source=source), code="""
    var data = source.data;
    var f = cb_obj.value
    x = data['x']
    y = data['y']
    for (i = 0; i < x.length; i++) {
        y[i] = Math.pow(x[i], f)
    }
    source.change.emit();
""")
# 滑动条
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power")
slider.js_on_change('value', callback)
# 列表显示
layout = column(slider, plot)
# 显示
show(layout)
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure
# 数据
x = [x*0.05 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
# 画布、绘图
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
# Python回调函数
def callback(source=source, window=None):
    data = source.data
    f = cb_obj.value
    x, y = data['x'], data['y']
    for i in range(len(x)):
        y[i] = window.Math.pow(x[i], f)
    source.change.emit()
# 滑动条数据交互
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power",
                callback=CustomJS.from_py_func(callback))
# 列表显示
layout = column(slider, plot)
# 显示
show(layout)
BokehDeprecationWarning: 'from_py_func' is deprecated and will be removed in an eventual 2.0 release. Use CustomJS directly instead.
from random import random
from bokeh.layouts import row
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.plotting import figure
# 数据
x = [random() for x in range(500)]
y = [random() for y in range(500)]
s1 = ColumnDataSource(data=dict(x=x, y=y))
# 画布1、绘图1
p1 = figure(plot_width=400, plot_height=400, tools="lasso_select", title="Select Here")
p1.circle('x', 'y', source=s1, alpha=0.6)
# 画布2、绘图2
s2 = ColumnDataSource(data=dict(x=[], y=[]))  # 初始数据为空
p2 = figure(plot_width=400, plot_height=400, x_range=(0, 1), y_range=(0, 1),
            tools="", title="Watch Here")
p2.circle('x', 'y', source=s2, alpha=0.6)  
# 回调函数
s1.callback = CustomJS(args=dict(s2=s2), code="""
        var inds = cb_obj.selected['1d'].indices;
        var d1 = cb_obj.data;
        var d2 = s2.data;
        d2['x'] = []
        d2['y'] = []
        for (i = 0; i < inds.length; i++) {
            d2['x'].push(d1['x'][inds[i]])
            d2['y'].push(d1['y'][inds[i]])
        }
        s2.change.emit();
    """)  # s2为套索选择的数据点
# 行显示
layout = row(p1, p2)
# 显示
show(layout)
from bokeh.models import CustomJS, Select, Column, ColumnDataSource
from bokeh.models.transforms import LinearInterpolator, StepInterpolator
# 数据
N = 600
controls = ColumnDataSource(data=dict(x=[1, 2, 3, 4, 5], y=[2, 8, 7, 3, 5]))
source = ColumnDataSource(data=dict(x=[], y=[]))
linear = LinearInterpolator(x='x', y='y', data=controls)
step = StepInterpolator(x='x', y='y', data=controls)
# 绘图
p = figure(x_range=(0, 6), y_range=(0, 10))
p.circle(x='x', y='y', source=controls, size=15, alpha=0.5, color="firebrick")
p.circle(x='x', y='y', source=source, size=1, alpha=0.2, color="navy")
callback = CustomJS(args=dict(source=source, linear=linear, step=step, N=N), code="""
    var mode = cb_obj.value;
    var data = source.data;
    var dx = 6 / N;

    if (mode == 'None') {
        data['x'] = [];
        data['y'] = [];
    } else {
        var interp;
        switch (mode) {
            case 'Linear':
                interp = linear;
                break;
            case 'Step (before)':
                interp = step;
                step.mode = 'before';
                break;
            case 'Step (center)':
                interp = step;
                step.mode = 'center';
                break;
            case 'Step (after)':
                interp = step;
                step.mode = 'after';
                break;
        }

        for (var i = 0; i < N; i++) {
            data['x'][i] = i * dx
            data['y'][i] = interp.compute(data['x'][i])
        }
    }

    source.change.emit()
""")

mode = Select(
    title='Interpolation Mode',
    value='None',
    options=['None', 'Linear', 'Step (before)', 'Step (center)', 'Step (after)'],
    callback=callback,
    width=300)
# 显示
show(Column(mode, p))

未经允许不得转载!Bokeh-视图属性