Python可视化(三)——Bokeh

Bokeh是一个Python库,用于为web浏览器创建交互式可视化。即其创建的可视化绘图展示在web浏览器上,并且用户能够用过web浏览器上的控件实现图形的交互式操作。

和matplotlib、seaborn等不同,Bokeh创建的图形由于能够进行交互式操作,因此更能够看到一些细节性的特征。并且Bokeh还支持JavaSctipt驱动的可视化,而无需编写JavaScript语句。

官网对该工具的介绍为:

Bokeh is a Python library for creating interactive visualizations for modern web browsers. It helps you build beautiful graphics, ranging from simple plots to complex dashboards with streaming datasets. With Bokeh, you can create JavaScript-powered visualizations without writing any JavaScript yourself.
官网地址为:https://bokeh.org/

这里主要利用jupyter进行演示,需要先调用如下内容:

from bokeh.plotting import output_notebook

output_notebook()

示例

虽然bokeh支持JavaSctipt驱动的可视化,但这里并不会涉及,只会说明一些基本的绘图接口和控件。

首先来看一个例子:

from bokeh.plotting import figure, show

x = list(range(1,11))
y = [5,1,7,3,8,4,9,2,10,6]

fig = figure(title = "Example", x_axis_label = "X Axis", y_axis_label = "Y Axis")
line1 = fig.line(x, y, legend_label = "blue",  line_width = 2)
line2 = fig.line(x, y[-1::-1], legend_label = "red", line_width = 2, line_color= "red")

show(fig)

在上面的代码中:

  • 首先创建了x和y两个列表,分别表示x轴和y轴的数据
  • 然后创建了figure对象,设置了标题和x轴、y轴的标签
  • 然后调用了line方法,分别绘制了蓝色和红色的线,线宽为2,图例分别为“blue”和“red”
  • 最后调用show方法进行显示
  • 在显示出来的图中,右侧的工具栏分别有:pan、box_zoom、wheel_zoom、save、reset、help等工具,可以对图进行缩放、保存等操作

在图表创建完成后,调用show函数后会生成对应的图表,并打开web浏览器显示生成的HTML文件。

这整个过程都是自动生成的,用户所做的只是编写上述的几行代码而已。

参数

从上面的示例可以看出,其实大部分的可视化工具的操作都是类似的操作:

  • 先创建符合要求的数据,一般是可迭代的
  • 然后创建画布,设置画布的属性,进行绘图,设置图表的属性,如图例、坐标轴等
  • 最后显示图像或者保存图像

不同的图像绘制区别在于图表不同调用的函数不同,如:

  • figure():figure() 函数用于创建一个新的图像对象,即 Figure 类的实例。可以通过指定参数来设置图像的大小、标题、坐标轴范围等。
  • line():line() 函数用于绘制折线图。它接受 x 值和 y 值的列表或数组,并将其绘制为折线。
  • scatter():scatter() 函数用于绘制散点图。它接受 x 值和 y 值的列表或数组,并将其绘制为散点。
  • bar():bar() 函数用于绘制柱状图。它接受类别和对应的数值,并将其绘制为柱状图。
  • hist():hist() 函数用于绘制直方图。它接受数值列表或数组,并将其绘制为直方图。
  • area():area() 函数用于绘制面积图。它接受 x 值和 y 值的列表或数组,并将其绘制为面积图。
  • boxplot():boxplot() 函数用于绘制箱线图。它接受数值列表或数组,并将其绘制为箱线图。

在上面的函数中,有一些参数是公共的,不过首先看一下figure()函数,它的参数都有:

  • width:图像的宽度,可以是像素值或百分比。默认值为 600。
  • height:图像的高度,可以是像素值或百分比。默认值为 400。
  • title:图像的标题。
  • x_range:x 坐标轴的范围。可以是一个包含两个值的列表或元组,例如 [0, 10]。如果不指定,则根据数据自动确定范围。
  • y_range:y 坐标轴的范围。可以是一个包含两个值的列表或元组,例如 [0, 100]。如果不指定,则根据数据自动确定范围。
  • x_axis_label:x 坐标轴的标签。
  • y_axis_label:y 坐标轴的标签。
  • x_axis_type:x 坐标轴的类型。可以是 'auto'、'linear'、'log'、'datetime' 或 'categorical'。
  • y_axis_type:y 坐标轴的类型。可以是 'auto'、'linear'、'log'、'datetime' 或 'categorical'。
  • tools:用于交互的工具集。可以是字符串、工具对象或工具列表。例如 'pan,box_zoom,reset'。
  • background_fill_color:背景颜色。
  • border_fill_color:边框颜色。
  • toolbar_location:工具栏的位置。可以是 'above'、'below'、'left'、'right' 或 None。
  • toolbar_sticky:工具栏是否固定在图像上。默认为 False。

以line函数为例,它的参数有:

  • x:折线图的 x 值。可以是列表、数组或其他可迭代对象。
  • y:折线图的 y 值。可以是列表、数组或其他可迭代对象。
  • line_color:折线的颜色。可以接受颜色名称或十六进制颜色代码。
  • line_width:折线的宽度。默认值为 1。
  • line_alpha:折线的透明度。可以是 0 到 1 之间的浮点数。默认值为 1。
  • line_dash:折线的虚线样式。可以是一个由实线和空格长度交替组成的列表,例如 [4, 4] 表示 4 个像素的实线和 4 个像素的空格。
  • line_cap:折线的端点样式。可以是 'butt'、'round' 或 'square'。默认值为 'butt'。
  • line_join:折线的连接点样式。可以是 'miter'、'round' 或 'bevel'。默认值为 'miter'。
  • legend_label:折线的图例标签。用于在图例中标识折线。
  • name:折线的名称。可以用于在 JavaScript 中引用折线。

另外如果想要单独改变某个图形的属性,可以使用glyphs的方法,比如:

glyph = line1.glyph
glyph.line_color = "yellow"

show(fig)

这样就将线条的颜色改为了黄色。

图例、文本和注解

有了上边的示例,就可以同样修改图例的属性了。不过图例的属性是在图例对象上修改的,而不是在图形对象上修改的:

fig.legend.location = "top_left"

fig.legend.title = "Legend"

fig.legend.label_text_font = "times"
fig.legend.label_text_font_style = "italic"
fig.legend.label_text_color = "navy"

fig.legend.border_line_width = 3
fig.legend.border_line_color = "navy"
fig.legend.border_line_alpha = 0.8
fig.legend.background_fill_color = "navy"
fig.legend.background_fill_alpha = 0.2

show(fig)

同样也可以修改标题的属性,不过标题属性是通过title属性来修改的:

fig.title_location = "left"

fig.title.text = "Title"

fig.title.text_font_size = "25px"
fig.title.align = "center"
fig.title.background_fill_color = "darkgrey"
fig.title.text_color = "white"

show(fig)

关于注解,Bokeh提供了一个annotation模块,可以用来添加注解。该模块位于bokeh.models.annotations中,主要包括以下几种注解:

  • Title(标题):用于在图表的顶部添加一个标题。
  • Label(标签):用于在图表中的指定位置添加文本标签。
  • Arrow(箭头):用于在图表中的指定位置添加箭头。
  • Rect(矩形框):用于在图表中指定的位置添加一个矩形框。
  • Span(横线/竖线):用于在图表中的指定位置添加一条横线或竖线。
  • BoxAnnotation(矩形范围):用于在图表中指定的区域添加一个矩形范围。

这些注解类可以通过创建相应的注解对象,并使用add_layout()方法将其添加到图表中。可以使用注解对象的属性和方法来定制注解的外观和行为。

这里以BoxAnnotation为例,看下相关的使用方法:

from bokeh.models import BoxAnnotation

bottom = BoxAnnotation(top=3, fill_alpha=0.2, fill_color='red')
mid = BoxAnnotation(bottom=3, top=8, fill_alpha=0.2, fill_color='green')
top = BoxAnnotation(bottom=8, fill_alpha=0.2, fill_color='red')

fig.add_layout(bottom)
fig.add_layout(mid)
fig.add_layout(top)

show(fig)

主题、坐标轴和网格线和工具条

bokeh中存在5个内置主题,分别是:

  • caliber
  • dark_minimal
  • light_minimal
  • night_sky
  • contrast

利用下面代码就可以修改当前使用的主题。

from bokeh.io import curdoc

curdoc().theme = 'light_minimal'

show(fig)

绘图的尺寸在figure()函数中通过width和height参数指定,单位为像素。默认值为600像素。

同时,还可以通过sizing_mode参数指定绘图的尺寸随浏览器窗口的变化而变化。sizing_mode参数的可选值有:

  • fixed:绘图的尺寸固定不变。
  • stretch_both:绘图的尺寸随浏览器窗口的变化而变化。
  • scale_width:绘图的宽度随浏览器窗口的变化而变化,高度固定不变。
  • scale_height:绘图的高度随浏览器窗口的变化而变化,宽度固定不变。
fig.sizing_mode = "fixed"

show(fig)

 坐标轴的一些属性可以通过下面的方法设置:

fig.xaxis.axis_label = "New X Axis Label"
fig.xaxis.axis_line_width = 3
fig.xaxis.axis_line_color = "red"

fig.yaxis.axis_label = "New Y Axis Label"
fig.yaxis.major_label_text_color = "orange"
fig.yaxis.major_label_orientation = "vertical"

fig.axis.minor_tick_in = -3
fig.axis.minor_tick_out = 6

# show the results
show(fig)

对坐标轴的定制则可以通过以下形式:

  • 坐标轴的范围可以通过设置figure对象的x_range和y_range属性来调整。
  • 坐标轴的类型可以通过设置figure对象的x_axis_type和y_axis_type属性来调整。
  • 坐标轴的刻度类型可以通过bokeh.models中的DatetimeTickFormatter和NumeralTickFormatter来实现。
from bokeh.models import DatetimeTickFormatter, NumeralTickFormatter

fig.xaxis.formatter = DatetimeTickFormatter(days="%m/%d", months="%Y/%m", hours="%H:%M", minutes="%H:%M")
fig.yaxis.formatter = NumeralTickFormatter(format="$0.0")

# show the results
show(fig)

如果要改变图形的网格线,或者添加辅助线的话,则需要借助于grid对象:

fig.xgrid.grid_line_color = "red"

fig.ygrid.grid_line_color = "black"
fig.ygrid.grid_line_alpha = 1
fig.ygrid.grid_line_dash = [6, 4]

show(fig)

或者可以这么显示:

fig.xgrid.bounds = (2, 8)

fig.ygrid.band_fill_color = "navy"
fig.ygrid.band_fill_alpha = 0.1

show(fig)

设置前景色和背景色则可以通过以下方式:

fig.background_fill_color = (204, 255, 255)
fig.border_fill_color = (102, 204, 255)
fig.outline_line_color = (0, 0, 255)

show(fig)

对于工具条来说,同样有一些属性可以设置:

  • fig.toolbar_location: 工具条的位置,可选值有:above, below, left, right
  • fig.toolbar.autohide: 是否自动隐藏工具条
  • fig.add_tools(): 添加工具
  • fig.remove_tools(): 删除工具

另外对于鼠标悬浮在工具条上的工具时出现的提示,也可以使用tooltips参数来实现。

图形的组合

在matplotlib和seaborn中,可以使用subplot()函数或者col参数将多个图形放在同一张图中。

而在bokeh中,可以简单是用column()或者row()函数将多个图形放在同一张图中。

from bokeh.layouts import column, row

x = list(range(1,11))
y = [5,1,7,3,8,4,9,2,10,6]

fig1 = figure(title = "Example", x_axis_label = "X Axis", y_axis_label = "Y Axis")
line1 = fig1.line(x, y, legend_label = "blue",  line_width = 2)

fig2 = figure(title = "Example", x_axis_label = "X Axis", y_axis_label = "Y Axis")
line2 = fig2.line(x, y[-1::-1], legend_label = "red", line_width = 2, line_color= "red")

show(row(fig1, fig2))
# show(column(fig1, fig2))

文件导出与保存

之前的所有示例都是使用show()函数显示的,从而实现在web浏览器中的显示,这一切都是自动实现的。

而bokeh也是可以将图形保存为其它文件类型或导出到本地的。该功能一般是通过output_file()函数实现的,该函数有几个参数:

  • filename:保存的文件名
  • title:文档标题,用于HTML中的<title>标签

而如果output_file函数没有使用自定义的文件名,bokeh默认使用当前运行的Python脚本的文件名作为HTML输出的文件名。如果脚本文件名不可用(例如,在Jupyter笔记本中),则bokeh将生成一个随机文件名。

由于调用show()函数时,会自动创建一个HTML文件,所以如果不想要显示图形,而只是想要保存图形,可以使用save()函数。

from bokeh.layouts import column, row
from bokeh.plotting import figure, output_file, save

x = list(range(1,11))
y = [5,1,7,3,8,4,9,2,10,6]

output_file("test.html",title="Example")


fig1 = figure(title = "Example", x_axis_label = "X Axis", y_axis_label = "Y Axis")
line1 = fig1.line(x, y, legend_label = "blue",  line_width = 2)

fig2 = figure(title = "Example", x_axis_label = "X Axis", y_axis_label = "Y Axis")
line2 = fig2.line(x, y[-1::-1], legend_label = "red", line_width = 2, line_color= "red")

save(row(fig1, fig2))

而之前的output_notebook()则是将图形在notebook中显示。

但其实保存到本地的话,用户可能更希望采用图片的形式,而如果要导出图片的话,就需要用到selenium库了,具体的代码如下:

from bokeh.io import export_png

export_png(fig, filename="test.png")

整体架构

上面提到的只是bokeh中最基本的画图功能,bokeh还可以实现UI的制作。

在bokeh中,各个模块协作,能够实现多种功能。bokeh中的主要接口有:

  • bokeh.plotting:绘图接口,提供了绘图的基本功能,可以绘制静态图或交互式图。
  • bokeh.models:模型接口,提供了丰富的图形组件,可以用于构建图形。
  • bokeh.layouts:布局接口,提供了多种布局方式,可以用于组合图形。
  • bokeh.io:输入输出接口,提供了多种输入输出方式,可以用于将图形保存到文件或者显示在网页中。
  • bokeh.themes:主题接口,提供了多种主题,可以用于美化图形。
  • bokeh.palettes:调色板接口,提供了多种调色板,可以用于数据可视化。
  • bokeh.server:服务接口,提供了多种服务方式,可以用于部署图形应用。

同时bokeh中的基本概念有:

  • Application:一个Bokeh应用程序是一个Python脚本,它创建一个或多个Bokeh文档。一个应用程序可以是一个单独的脚本,也可以是一个目录,其中包含一个main.py脚本和其他资源文件。
  • Document:Bokeh文档是一个包含Bokeh模型的树结构,这些模型将在浏览器中呈现。Bokeh文档可以从Python创建,也可以从一个或多个应用程序脚本中加载。
  • Models:Bokeh模型是Bokeh库中的一个Python对象,它们知道如何在浏览器中呈现。每个模型都有许多属性,用于控制其外观和行为。
  • Layouts:布局是Bokeh模型的组合,用于在单个文档中创建多个图表。布局可以嵌套,以创建更复杂的布局。
  • Widgets:小部件是与Python回调关联的事件处理程序,这些回调可以修改文档中的数据、属性或布局。
  • Server:Bokeh服务器是一个Python进程,它协调Bokeh文档的呈现和事件处理。
  • Glyphs:Bokeh中的基本绘图原语。例如,一个圆形是一个由x、y、size和颜色属性控制的圆形。
  • BokehJS:BokehJS是一个JavaScript库,它包含用于呈现和交互的低级别模型,以及用于构建高级模型和小部件的高级模型。
  • Embedding:将Bokeh文档嵌入到网站或应用程序中的过程。

bokeh serve和Widgets

首先看一个示例:

''' Present an interactive function explorer with slider widgets.

Scrub the sliders to change the properties of the ``sin`` curve, or
type into the title text box to update the title of the plot.

Use the ``bokeh serve`` command to run the example by executing:

    bokeh serve sliders.py

at your command prompt. Then navigate to the URL

    http://localhost:5006/sliders

in your browser.

'''
import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Slider, TextInput
from bokeh.plotting import figure

# Set up data
N = 200
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))


# Set up plot
plot = figure(height=400, width=400, title="my sine wave",
              tools="crosshair,pan,reset,save,wheel_zoom",
              x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])

plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)


# Set up widgets
text = TextInput(title="title", value='my sine wave')
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0, step=0.1)
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1, step=0.1)


# Set up callbacks
def update_title(attrname, old, new):
    plot.title.text = text.value

text.on_change('value', update_title)

def update_data(attrname, old, new):

    # Get the current slider values
    a = amplitude.value
    b = offset.value
    w = phase.value
    k = freq.value

    # Generate the new curve
    x = np.linspace(0, 4*np.pi, N)
    y = a*np.sin(k*x + w) + b

    source.data = dict(x=x, y=y)

for w in [offset, amplitude, phase, freq]:
    w.on_change('value', update_data)


# Set up layouts and add to document
inputs = column(text, offset, amplitude, phase, freq)

curdoc().add_root(row(inputs, plot, width=800))
curdoc().title = "Sliders"

首先保存上述代码到sliders.py文件中,然后在命令行中执行:

bokeh serve sliders.py

然后在浏览器中导航到URL:http://localhost:5006/sliders,可以看到对应的图形界面。

在上述代码中,使用的不再是figure()函数,而是curdoc()函数,这是因为在bokeh serve模式下,需要使用curdoc()函数来创建文档。

Document对象相当于是整个网页,而curdoc()函数返回的是当前的Document对象。而show()函数可以显示的内容却不能为Document对象,因此只能在命令行中调用bokeh serve命令来显示。

def show(obj: LayoutDOM | Application | ModifyDoc, browser: str | None = None, new: BrowserTarget = "tab",
        notebook_handle: bool = False, notebook_url: str = "localhost:8888", **kwargs: Any) -> CommsHandle | None:
    ''' Immediately display a Bokeh object or application.

        :func:`show` may be called multiple times in a single Jupyter notebook
        cell to display multiple objects. The objects are displayed in order.

    Args:
        obj (LayoutDOM or Application or callable) :
            A Bokeh object to display.

            Bokeh plots, widgets, layouts (i.e. rows and columns) may be
            passed to ``show`` in order to display them. If |output_file|
            has been called, the output will be to an HTML file, which is also
            opened in a new browser window or tab. If |output_notebook|
            has been called in a Jupyter notebook, the output will be inline
            in the associated notebook output cell.

            In a Jupyter notebook, a Bokeh application or callable may also
            be passed. A callable will be turned into an Application using a
            ``FunctionHandler``. The application will be run and displayed
            inline in the associated notebook output cell.

        browser (str, optional) :
            Specify the browser to use to open output files(default: None)

            For file output, the **browser** argument allows for specifying
            which browser to display in, e.g. "safari", "firefox", "opera",
            "windows-default". Not all platforms may support this option, see
            the documentation for the standard library
            :doc:`webbrowser <python:library/webbrowser>` module for
            more information

        new (str, optional) :
            Specify the browser mode to use for output files (default: "tab")

            For file output, opens or raises the browser window showing the
            current output file.  If **new** is 'tab', then opens a new tab.
            If **new** is 'window', then opens a new window.

        notebook_handle (bool, optional) :
            Whether to create a notebook interaction handle (default: False)

            For notebook output, toggles whether a handle which can be used
            with ``push_notebook`` is returned. Note that notebook handles
            only apply to standalone plots, layouts, etc. They do not apply
            when showing Applications in the notebook.

        notebook_url (URL, optional) :
            Location of the Jupyter notebook page (default: "localhost:8888")

            When showing Bokeh applications, the Bokeh server must be
            explicitly configured to allow connections originating from
            different URLs. This parameter defaults to the standard notebook
            host and port. If you are running on a different location, you
            will need to supply this value for the application to display
            properly. If no protocol is supplied in the URL, e.g. if it is
            of the form "localhost:8888", then "http" will be used.

            ``notebook_url`` can also be a function that takes one int for the
            bound server port.  If the port is provided, the function needs
            to generate the full public URL to the bokeh server.  If None
            is passed, the function is to generate the origin URL.

    Some parameters are only useful when certain output modes are active:

    * The ``browser`` and ``new`` parameters only apply when |output_file|
      is active.

    * The ``notebook_handle`` parameter only applies when |output_notebook|
      is active, and non-Application objects are being shown. It is only
      supported in Jupyter notebook and raises an exception for other notebook
      types when it is True.

    * The ``notebook_url`` parameter only applies when showing Bokeh
      Applications in a Jupyter notebook.

    * Any additional keyword arguments are passed to :class:`~bokeh.server.Server` when
      showing a Bokeh app (added in version 1.1)

    Returns:
        When in a Jupyter notebook (with |output_notebook| enabled)
        and ``notebook_handle=True``, returns a handle that can be used by
        ``push_notebook``, None otherwise.

    '''
    state = curstate()

    if isinstance(obj, LayoutDOM):
        return _show_with_state(obj, state, browser, new, notebook_handle=notebook_handle)

    def is_application(obj: Any) -> TypeGuard[Application]:
        return getattr(obj, '_is_a_bokeh_application_class', False)

    if is_application(obj) or callable(obj): # TODO (bev) check callable signature more thoroughly
        # This ugliness is to prevent importing bokeh.application (which would bring
        # in Tornado) just in order to show a non-server object
        assert state.notebook_type is not None
        return run_notebook_hook(state.notebook_type, 'app', obj, state, notebook_url, **kwargs)

    raise ValueError(_BAD_SHOW_MSG)

但很明显,在可视化脚本完成后,还需要在命令行中执行bokeh serve xxx.py,才能在浏览器中看到结果。对于操作来说并不十分方便,因此bokeh提供了一种这样的方法:

https://docs.bokeh.org/en/latest/docs/user_guide/server/app.html

这里再看一个示例:

# app.py

from bokeh.plotting import curdoc, figure
from bokeh.server.server import Server
from bokeh.application.application import Application
from bokeh.application.handlers.directory import DirectoryHandler

class UI:
    def run(self):

        fig = figure(height=400, width=400, title="test")
        fig.line(x = [1,4,8,6,9], y = [5,7,8,5,3], line_width=3, line_alpha=0.6)
        
        curdoc().add_root(fig)

if __name__ == "__main__":
    starting_port = 5566
    app = Application(DirectoryHandler(filename = "."))

    server = Server(app, num_procs=1, port=starting_port, check_unused_sessions_milliseconds=2000, unused_session_lifetime_milliseconds=1000)
    server.start()

    server.io_loop.add_callback(server.show, "/")
    server.io_loop.start()
# main.py

from app import UI

ui = UI()
ui.run()

运行app.py后,bokeh会自动检测当前目录,找到并处理main.py,并会在浏览器窗口中打开页面,显示上面代码中定义的document对象。

而至于bokeh serve和Widgets的详细使用说明,官方文档中给出了详细的API文档和参数说明,这里就不再赘述了。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值