添加批注

Bokeh包括几种不同类型的注释,允许用户在可视化中添加补充信息。

标题

Title 注释允许在打印边缘周围呈现描述性文本。

使用时 bokeh.plottingbokeh.Charts ,添加基本标题的最快方法是将文本作为 title 参数到 Figure 或任何图表函数:

from bokeh.plotting import figure, output_file, show

p = figure(title="Basic Title", plot_width=300, plot_height=300)
p.circle([1,2], [3,4])

output_file("title.html")

show(p)

默认标题通常位于绘图的顶部,与左侧对齐。但是默认标题出现在绘图的哪一侧可以由 title_location 参数:

from bokeh.plotting import figure, output_file, show

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)

默认值 Title 可通过 Plot.title 财产。字体、边框、背景和其他视觉属性可以直接在 .title . 下面是一个使用设置字体和背景属性以及标题文本和标题对齐方式的示例 .title

from bokeh.plotting import figure, output_file, show

p = figure(plot_width=300, plot_height=300)
p.circle([1,2], [3,4])

# configure visual properties on a plot's title attribute
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)

请注意,对齐是沿着文本方向测量的。例如,对于地块左侧的标题,“left”将位于下角。

除了默认标题之外,还可以创建和添加其他 Title 对象以使用 add_layout 绘图方法:

from bokeh.models import Title
from bokeh.plotting import figure, output_file, show

p = figure(title="Left Title", title_location="left",
           plot_width=300, plot_height=300)
p.circle([1,2], [3,4])

# add extra titles with add_layout(...)
p.add_layout(Title(text="Bottom Centered Title", align="center"), "below")

output_file("title.html")

show(p)

如果标题和粘滞工具栏设置在同一侧,它们将占用相同的空间:

from bokeh.plotting import figure, output_file, show

p = figure(title="Top Title with Toolbar", toolbar_location="above",
           plot_width=600, plot_height=300)
p.circle([1,2], [3,4])

output_file("title.html")

show(p)

如果打印大小足够大,则可以生成更紧凑的打印。但是,如果打印大小不够大,标题和工具栏可能会以不理想的方式在视觉上重叠。

传说

有可能创造 Legend 通过在创建绘图时为glyph方法指定图例参数,可以轻松地进行注释。

基本图例标签

要为glyph提供一个简单的显式标签,请传递 legend_label 关键字参数:

p.circle('x', 'y', legend_label="some label")

如果多个glyph被赋予同一个标签,它们将被合并到一个带有该标签的图例项中。

import numpy as np

from bokeh.plotting import figure, output_file, show

x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)

output_file("legend.html")

p = figure()

p.circle(x, y, legend_label="sin(x)")
p.line(x, y, legend_label="sin(x)")

p.line(x, 2*y, legend_label="2*sin(x)",
       line_dash=[4, 4], line_color="orange", line_width=2)

p.square(x, 3*y, legend_label="3*sin(x)", fill_color=None, line_color="green")
p.line(x, 3*y, legend_label="3*sin(x)", line_color="green")

show(p)

自动分组(Python)

通常需要通过对数据源列中的值进行分组来生成多个图例项。Bokeh可以通过传递 legend_group glyph方法的关键字参数:

p.circle('x', 'y', legend_group="colname", source=source)

使用此方法时,将立即在Python中执行分组,随后的Python代码将能够在中查看各个图例项 Legend.items 财产。如果需要,可以重新排列或修改这些项目。

from bokeh.io import show
from bokeh.models import ColumnDataSource
from bokeh.palettes import RdBu3
from bokeh.plotting import figure

c1 = RdBu3[2] # red
c2 = RdBu3[0] # blue
source = ColumnDataSource(dict(
    x=[1, 2, 3, 4, 5, 6],
    y=[2, 1, 2, 1, 2, 1],
    color=[c1, c2, c1, c2, c1, c2],
    label=['hi', 'lo', 'hi', 'lo', 'hi', 'lo']
))

p = figure(x_range=(0, 7), y_range=(0, 3), plot_height=300, tools='save')

# legend field matches the column in the source
p.circle( x='x', y='y', radius=0.5, color='color', legend_group='label', source=source)

show(p)

注解

要使用此功能,一个 source 参数 还必须提供 到glyph方法。此外,要分组的列必须已经存在于数据源中。

自动分组(浏览器)

也可以在浏览器中指定分组发生在JavaScript端。这可能是可取的,例如,如果分组发生在只在JavaScript端计算的列上。

p.circle('x', 'y', legend_field="colname", source=source)

在本例中,Python代码 not 查看中的多个项目 Legend.items . 相反,只有一个项目表示要在浏览器中执行的分组。

from bokeh.io import show
from bokeh.models import ColumnDataSource
from bokeh.palettes import RdBu3
from bokeh.plotting import figure

c1 = RdBu3[2] # red
c2 = RdBu3[0] # blue
source = ColumnDataSource(dict(
    x=[1, 2, 3, 4, 5, 6],
    y=[2, 1, 2, 1, 2, 1],
    color=[c1, c2, c1, c2, c1, c2],
    label=['hi', 'lo', 'hi', 'lo', 'hi', 'lo']
))

p = figure(x_range=(0, 7), y_range=(0, 3), plot_height=300, tools='save')

# legend field matches the column in the source
p.circle( x='x', y='y', radius=0.5, color='color', legend_field='label', source=source)

show(p)

手动图例

也可以不指定任何图例参数,而手动生成 Legend 用手。这方面的一个例子可以在 examples/models/file/legends.py

显式索引

其他时候,显式地告诉Bokeh ColumnDataSource 应在绘制图例项时使用。尤其是,如果要为“多”字形绘制多个图例项,例如 MultiLinePatches . 这是通过指定 index 对于图例项,如下所示。

from bokeh.models import Legend, LegendItem
from bokeh.plotting import figure, show

p = figure()
r = p.multi_line([[1,2,3], [1,2,3]], [[1,3,2], [3,4,3]],
                 color=["orange", "red"], line_width=4)

legend = Legend(items=[
    LegendItem(label="orange", renderers=[r], index=0),
    LegendItem(label="red", renderers=[r], index=1),
])
p.add_layout(legend)

show(p)

交互式图例

也可以将图例配置为交互式的,这样单击或点击图例条目会影响相应的字形可见性。见 交互式图例 有关更多信息和示例,请参阅用户指南的一节。

注解

交互式图例 功能目前适用于第一个“每字形”样式图例。通过指定要自动分组的列创建的图例尚不支持交互式功能。

颜色条

A ColorBar 可以使用 ColorMapper 实例,其中包含调色板。同时支持on和off打印颜色条;在添加 ColorBar 对情节的影响。

注解

这个示例依赖于开源NumPy库来生成演示数据。

import numpy as np

from bokeh.models import ColorBar, LogColorMapper, LogTicker
from bokeh.plotting import figure, output_file, show

output_file('color_bar.html')

def normal2d(X, Y, sigx=1.0, sigy=1.0, mux=0.0, muy=0.0):
    z = (X-mux)**2 / sigx**2 + (Y-muy)**2 / sigy**2
    return np.exp(-z/2) / (2 * np.pi * sigx * sigy)

X, Y = np.mgrid[-3:3:100j, -2:2:100j]
Z = normal2d(X, Y, 0.1, 0.2, 1.0, 1.0) + 0.1*normal2d(X, Y, 1.0, 1.0)
image = Z * 1e6

color_mapper = LogColorMapper(palette="Viridis256", low=1, high=1e7)

plot = figure(x_range=(0,1), y_range=(0,1), toolbar_location=None)
plot.image(image=[image], color_mapper=color_mapper,
           dh=[1.0], dw=[1.0], x=[0], y=[0])

color_bar = ColorBar(color_mapper=color_mapper, ticker=LogTicker(),
                     label_standoff=12, border_line_color=None, location=(0,0))

plot.add_layout(color_bar, 'right')

show(plot)

箭头

Arrow 注释可用于连接图示符和标签注释,或仅高亮显示打印区域。箭头是复合注释,这意味着 startend 属性本身就是另一个 ArrowHead 注解。默认情况下 Arrow 注释与 end 设置为 OpenHead -键入arrowhead(一种开放式背面楔形样式)和 start 属性设置为 None . 可以通过设置 startend 适当的属性 ArrowHead 子类实例。

箭头具有用于设置箭头轴的颜色和外观的标准线属性:

my_arrow.line_color = "blue"
my_arrow.line_alpha = 0.6

箭头也可以配置为引用其他非默认的x或y范围 x_rangey_range 属性,与 双斧 .

此外,任何箭头对象 startend 有一个 size 属性控制箭头的大小,以及直线和填充属性。线属性控制箭头的轮廓,填充属性控制箭头的内部(如果适用)。

from bokeh.models import Arrow, NormalHead, OpenHead, VeeHead
from bokeh.plotting import figure, output_file, show

output_file("arrow.html", title="arrow.py example")

p = figure(plot_width=600, plot_height=600)

p.circle(x=[0, 1, 0.5], y=[0, 0, 0.7], radius=0.1,
         color=["navy", "yellow", "red"], fill_alpha=0.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))

p.add_layout(Arrow(end=NormalHead(fill_color="orange"),
                   x_start=1, y_start=0, x_end=0.5, y_end=0.7))

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)

乐队

A Band 将创建一个维度链接的“条纹”,位于数据或屏幕坐标中。波段注释的一个常见用途是指示与一系列测量相关的不确定度。

import numpy as np
import pandas as pd

from bokeh.models import Band, ColumnDataSource
from bokeh.plotting import figure, output_file, show

output_file("band.html", title="band.py example")

# Create some random data
x = np.random.random(2500) * 140 - 20
y = np.random.normal(size=2500) * 2 + 5

df = pd.DataFrame(data=dict(x=x, y=y)).sort_values(by="x")

sem = lambda x: x.std() / np.sqrt(x.size)
df2 = df.y.rolling(window=100).agg({"y_mean": np.mean, "y_std": np.std, "y_sem": sem})
df2 = df2.fillna(method='bfill')

df = pd.concat([df, df2], axis=1)
df['lower'] = df.y_mean - df.y_std
df['upper'] = df.y_mean + df.y_std

source = ColumnDataSource(df.reset_index())

TOOLS = "pan,wheel_zoom,box_zoom,reset,save"
p = figure(tools=TOOLS)

p.scatter(x='x', y='y', line_color=None, fill_alpha=0.3, size=5, source=source)

band = Band(base='x', lower='lower', upper='upper', source=source, level='underlay',
            fill_alpha=1.0, line_width=1, line_color='black')
p.add_layout(band)

p.title.text = "Rolling Standard Deviation"
p.xgrid[0].grid_line_color=None
p.ygrid[0].grid_line_alpha=0.5
p.xaxis.axis_label = 'X'
p.yaxis.axis_label = 'Y'

show(p)

方框批注

A BoxAnnotation 可以链接到数据或屏幕坐标,以强调特定的绘图区域。默认情况下,框注释尺寸(例如。 lefttop )将注释扩展到绘图区域的边缘。

from bokeh.models import BoxAnnotation
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.glucose import data

output_file("box_annotation.html", title="box_annotation.py example")

TOOLS = "pan,wheel_zoom,box_zoom,reset,save"

#reduce data size
data = data.loc['2010-10-06':'2010-10-13']

p = figure(x_axis_type="datetime", tools=TOOLS)

p.line(data.index.to_series(), data['glucose'], line_color="gray", line_width=1, legend_label="glucose")

low_box = BoxAnnotation(top=80, fill_alpha=0.1, fill_color='red')
mid_box = BoxAnnotation(bottom=80, top=180, fill_alpha=0.1, fill_color='green')
high_box = BoxAnnotation(bottom=180, fill_alpha=0.1, fill_color='red')

p.add_layout(low_box)
p.add_layout(mid_box)
p.add_layout(high_box)

p.title.text = "Glucose Range"
p.xgrid[0].grid_line_color=None
p.ygrid[0].grid_line_alpha=0.5
p.xaxis.axis_label = 'Time'
p.yaxis.axis_label = 'Value'

show(p)

标签

标签是文本元素,可用于注释字形或打印区域。

要创建单个文本标签,请使用 Label 注释。此批注配置为 text 属性包含要显示的文本,以及 xy 属性设置位置(以屏幕或数据空间单位为单位)。另外,渲染模式 "canvas""css" 可以指定。最后,标签 textborder_linebackground_fill 属性。这些控件控制文本的视觉外观以及文本边框和背景:

Label(x=70, y=70, x_units='screen', text='Some Stuff', render_mode='css',
      border_line_color='black', border_line_alpha=1.0,
      background_fill_color='white', background_fill_alpha=1.0)

要同时创建多个标签,可能要轻松地注释另一个现有的glyph,请使用 LabelSet 注释,它配置了一个数据源,其中 textxy 位置以列名的形式给出。 LabelSet 对象也可以 x_offsety_offset ,以屏幕空间单位指定标签位置的偏移距离 xy . 最后,渲染级别可以用 level 属性,若要将标签放置在其他渲染器的上方或下方,请执行以下操作:

LabelSet(x='x', y='y', text='names', level='glyph',
         x_offset=5, y_offset=5, source=source)

以下示例说明了两者的用法:

from bokeh.models import ColumnDataSource, Label, LabelSet, Range1d
from bokeh.plotting import figure, output_file, show

output_file("label.html", title="label.py example")

source = ColumnDataSource(data=dict(height=[66, 71, 72, 68, 58, 62],
                                    weight=[165, 189, 220, 141, 260, 174],
                                    names=['Mark', 'Amir', 'Matt', 'Greg',
                                           'Owen', 'Juan']))

p = figure(title='Dist. of 10th Grade Students at Lee High',
           x_range=Range1d(140, 275))
p.scatter(x='weight', y='height', size=8, source=source)
p.xaxis[0].axis_label = 'Weight (lbs)'
p.yaxis[0].axis_label = 'Height (in)'

labels = LabelSet(x='weight', y='height', text='names', level='glyph',
              x_offset=5, y_offset=5, source=source, render_mode='canvas')

citation = Label(x=70, y=70, x_units='screen', y_units='screen',
                 text='Collected by Luke C. 2016-04-01', render_mode='css',
                 border_line_color='black', border_line_alpha=1.0,
                 background_fill_color='white', background_fill_alpha=1.0)

p.add_layout(labels)
p.add_layout(citation)

show(p)

斜坡

Slope 注释是可以倾斜并延伸到打印区域边缘的线。

import numpy as np

from bokeh.models import Slope
from bokeh.plotting import figure, output_file, show

output_file("slope.html", title="slope.py example")

# linear equation parameters
gradient = 2
y_intercept = 10

# create random data
xpts = np.arange(0, 20)
ypts = gradient * xpts + y_intercept + np.random.normal(0, 4, 20)

p = figure(plot_width=450, plot_height=450, y_range=(0, 1.1 * max(ypts)))

p.circle(xpts, ypts, size=5, color="skyblue")

slope = Slope(gradient=gradient, y_intercept=y_intercept,
              line_color='orange', line_dash='dashed', line_width=3.5)

p.add_layout(slope)

p.yaxis.axis_label = 'y'
p.xaxis.axis_label = 'x'

show(p)

跨距

Span 注释是具有单一尺寸(宽度或高度)并延伸到打印区域边缘的线。

import time
from datetime import datetime as dt

from bokeh.models import Span
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.daylight import daylight_warsaw_2013

output_file("span.html", title="span.py example")

p = figure(x_axis_type="datetime", y_axis_type="datetime")

p.line(daylight_warsaw_2013.Date, daylight_warsaw_2013.Sunset,
       line_dash='solid', line_width=2, legend_label="Sunset")
p.line(daylight_warsaw_2013.Date, daylight_warsaw_2013.Sunrise,
       line_dash='dotted', line_width=2, legend_label="Sunrise")

start_date = time.mktime(dt(2013, 3, 31, 2, 0, 0).timetuple())*1000
daylight_savings_start = Span(location=start_date,
                              dimension='height', line_color='green',
                              line_dash='dashed', line_width=3)
p.add_layout(daylight_savings_start)

end_date = time.mktime(dt(2013, 10, 27, 3, 0, 0).timetuple())*1000
daylight_savings_end = Span(location=end_date,
                            dimension='height', line_color='red',
                            line_dash='dashed', line_width=3)
p.add_layout(daylight_savings_end)

p.title.text = "2013 Sunrise and Sunset times in Warsaw"
p.yaxis.axis_label = 'Time of Day'

show(p)

络腮胡子

A Whisker 将创建一个维度链接的“茎”,位于数据或屏幕坐标中。在一个点上指示测量的误差或不确定度是触须注释的一个常见用途。

from bokeh.models import ColumnDataSource, Whisker
from bokeh.plotting import figure, show
from bokeh.sampledata.autompg import autompg as df

colors = ["red", "olive", "darkred", "goldenrod", "skyblue", "orange", "salmon"]

p = figure(plot_width=600, plot_height=300, title="Years vs mpg with Quartile Ranges")

base, lower, upper = [], [], []

for i, year in enumerate(list(df.yr.unique())):
    year_mpgs = df[df['yr'] == year]['mpg']
    mpgs_mean = year_mpgs.mean()
    mpgs_std = year_mpgs.std()
    lower.append(mpgs_mean - mpgs_std)
    upper.append(mpgs_mean + mpgs_std)
    base.append(year)

source_error = ColumnDataSource(data=dict(base=base, lower=lower, upper=upper))

p.add_layout(
    Whisker(source=source_error, base="base", upper="upper", lower="lower")
)

for i, year in enumerate(list(df.yr.unique())):
    y = df[df['yr'] == year]['mpg']
    color = colors[i % len(colors)]
    p.circle(x=year, y=y, color=color)

show(p)