Bokeh教程:基本绘图
本文翻译自 bokeh/bokeh-notebooks 项目,并经过修改。
本节介绍 bokeh.plotting
接口。
该接口是“中级”接口,主要思想可以用以下语句描述:
从简单的默认图形(使用默认工具,网格和轴)开始,添加视觉效果直接与数据相关联的标记和其他形状。
我们将会看到可以自定义和更改所有默认值,但是拥有默认值意味着可以非常快速地启动并运行。
导入和设置
当使用 bokeh.plotting
接口时,有一些常用的导入:
- 使用
figure
函数创建新的绘图对象 - 使用函数
output_file
或output_notebook
(可以同时使用)告诉 Bokeh 如何显示或保存图像 - 执行
show
或save
用于显示或保存绘图和布局
import numpy as np # 后面会使用,这里先导入
import pandas as pd
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
本示例中,我们使用 Jupyter notebook,所以下面我们调用 output_notebok()
。
我们只需要调用一次,随后所有对 show()
的调用都会在 notebook 中内联显示。
output_notebook()
如果一切正常,则应该看到 Bokeh 徽标和类似下面的消息作为输出。
BokehJS 2.0.1 successfully loaded.
实战数据
使用 CSSEGISandData/COVID-19 项目提供的数据集。
为了提高加载速度,本示例默认文档已保存到本地的 data 目录中,请从 URL 下载原始文件。
df_confirmed = pd.read_csv(
"data/time_series_covid19_confirmed_global.csv",
)
df_confirmed.head()
筛选数据,删掉不需要的列,并调整索引。
df_confirmed_selected = df_confirmed.loc[
df_confirmed["Country/Region"] == "China"
].copy()
df_confirmed_selected.index = df_confirmed_selected["Province/State"]
df_confirmed_selected.drop(
["Province/State", "Country/Region", "Lat", "Long"],
axis=1,
inplace=True
)
df_confirmed_selected.columns = [
pd.to_datetime(col) for col in df_confirmed_selected.columns
]
df_confirmed_selected.head()
选取一周的数据。
df_confirmed_week = df_confirmed_selected.loc[
:, pd.to_datetime("2020-02-17"):pd.to_datetime("2020-02-23")
]
df_confirmed_week.head()
散点图(Scatter Plots)
Bokeh 可以绘制许多类型的视觉形状(称为 glyphs),包括线条(lines),条形(bars),小块(pathes),六角瓦片(hex tiles)等等。 最常见的可视化任务之一是使用小 标记(marker) 符号来代表每个点,以绘制数据散点图。
在本节中,您将看到如何使用 Bokeh 的各种标记符号创建简单的散点图。
基本方法是:
- 创建一个空白的图形:
p = figure(...)
- 在图形中调用一个 glyph 方法,例如
p.circle
- 调用
show
显示图形
筛选数据,随机选取 5 个在 2020 年 2 月 17 日确诊人数在 100 人到 500 人之间的地区,并使用数字替换原有的索引值。
data = df_confirmed_week[pd.to_datetime("2020-02-17")].copy()
data = data[data.between(100, 500)].sample(n=5, random_state=1)
data.index = np.arange(len(data))
data
0 146
1 121
2 457
3 238
4 130
Name: 2020-02-17 00:00:00, dtype: int64
执行以下单元格,创建带有圆形(circle)标记的散点图:
# 使用 figure 创建使用默认工具的新图形
p = figure(plot_width=400, plot_height=400)
# 添加圆形渲染器,指定 x 和 y 坐标,大小,颜色和透明度
p.circle(
[0, 1, 2, 3, 4], # 或使用 data_confirmed.index 代替
[146, 121, 457, 238, 130], # 或使用 data_confirmed 代替
size=15,
line_color="blue",
fill_color="green",
fill_alpha=0.3
)
show(p) # 显示绘图结果
在上面的输出中,您可以看到 line_color
,fill_alpha
等不同选项的效果。
尝试更改其中一些值并重新执行单元格以更新绘图。
p = figure(plot_width=400, plot_height=400)
p.circle(
data.index,
data,
size=15,
line_color="red",
fill_color="yellow",
fill_alpha=0.8
)
show(p)
所有 Bokeh 散点图的标记都接受size
(以屏幕空间单位为单位)作为属性。
圆形标记也有 radius
(以“数据”空间单位测量)属性。
想要绘制正方形(square)标记而不是圆形,可以在图形上使用 square
方法。
p = figure(plot_width=400, plot_height=400)
p.square(
data.index,
data,
size=[10, 15, 20, 25, 30],
color="firebrick",
alpha=0.6,
)
show(p)
注意,在上面的示例中,我们还为每个单独的标记指定了不同的大小。
通常,可以用这种方式将 glyph 的所有属性“矢量化”。
还要注意,我们已经使用 color
属性,以便轻松地同时设置线条和填充颜色。
这是 bokeh.plotting
特有的便利。
Bokeh 中有许多可用的标记类型,您可以在单击下面列出的参考指南条目,查看所有标记的详细信息和示例:
- asterisk()
- circle()
- circle_cross()
- circle_x()
- cross()
- diamond()
- diamond_cross()
- hex()
- inverted_triangle()
- square()
- square_cross()
- square_x()
- triangle()
- x()
练习
使用 CSSEGISandData/COVID-19 项目提供的另一个数据集。
为了提高加载速度,本示例默认文档已保存到本地的 data 目录中,请从 URL 下载原始文件。
df_daily = pd.read_csv("data/02-17-2020.csv")
df_daily.head()
对数据进行过滤,选择确诊人数在 100 到 500 的地区。
df_daily_a = df_daily[df_daily["Country/Region"] == "Mainland China"]
df_daily_selected = df_daily_a[df_daily_a["Confirmed"].between(100, 500)]
df_daily_selected.head()
绘制确诊人数和治愈人数的散点图。
p = figure(
plot_width=400,
plot_height=400,
)
p.square(
df_daily_selected["Confirmed"],
df_daily_selected["Recovered"],
color="blue",
size=8,
alpha=0.5,
)
show(p)
线条图
另一个常见的可视化任务是绘制线条图。
可以在 Bokeh 中通过调用 p.line(...)
glyph 方法来实现,如下所示。
筛选数据,选择某地区一周的累积确诊人数。
data = df_confirmed_week.loc["Beijing"]
data.index = np.arange(len(data))
data
0 381
1 387
2 393
3 395
4 396
5 399
6 399
Name: Beijing, dtype: int64
p = figure(
plot_width=400,
plot_height=400,
title="线条图"
)
# 添加线条渲染器
p.line(
[0, 1, 2, 3, 4, 5, 6], # 或使用 data.index 代替
[381, 387, 393, 395, 396, 399, 399], # 或使用 data 代替
line_width=2
)
show(p)
除了 line_width
外,还有其他选项可以设置,例如 line_color
或 line_dash
。
尝试设置 line 的其他一些属性,然后重新运行上面的单元格。
p = figure(
plot_width=400,
plot_height=400,
title="使用点划线的线条图"
)
p.line(
data.index,
data,
line_width=2,
line_color="green",
line_dash="dotdash",
)
show(p)
日期时间坐标轴
时间序列数据通常由画线展示。
例如,示例数据的列索引都是日期时间类型,我们将其转置,这样每行代表一天,每列代表一个地区。
data = df_confirmed_selected.T
data.head()
我们想绘制此数据的一个子集,并且搭配一个合适的日期时间坐标轴。
我们可以通过向 figure
函数调用传递 x_axis_type ="datetime"
来向 Bokeh 请求日期时间轴。
下面代码中还配置了一些其他选项,例如图形尺寸,轴标题和网格线属性。
# 选取一个月的数据
month = data.loc[pd.to_datetime('2020-02-01'):pd.to_datetime('2020-02-29')]
p = figure(
x_axis_type="datetime",
title="Confirmed",
plot_height=350,
plot_width=800
)
p.xgrid.grid_line_color=None
p.ygrid.grid_line_alpha=0.5
p.xaxis.axis_label = 'Date'
p.yaxis.axis_label = 'Case'
p.line(month.index, month.Beijing)
show(p)
练习
绘制多条曲线。
p = figure(
x_axis_type="datetime",
title="Plot",
plot_height=350,
plot_width=800
)
p.xgrid.grid_line_color=None
p.ygrid.grid_line_alpha=0.5
p.xaxis.axis_label = 'Date'
p.yaxis.axis_label = 'Case'
p.line(
month.index,
month.Beijing,
color="green"
)
p.line(
month.index,
month.Shanghai,
color="red",
)
p.line(
month.index,
month.Tianjin,
color="blue",
)
show(p)
六角瓦片(Hex Tiling)
Bokeh 支持使用轴向坐标和 hex_tile
方法绘制低级 hex tilings,如用户指南的 Hex Tiles 部分所述。
但是,六角瓦片的最常见用途之一是可视化分箱。
Bokeh 将此通用操作封装在 hexbin
函数中,该函数的输出可以直接传递到 hex_tile
,如下所示。
from bokeh.palettes import Viridis256
from bokeh.util.hex import hexbin
n = 50000
x = np.random.standard_normal(n)
y = np.random.standard_normal(n)
bins = hexbin(x, y, 0.1)
# 手动为分箱分配颜色,后续将会看到如何使用 linear_cmap
color = [Viridis256[int(i)] for i in bins.counts/max(bins.counts)*255]
# match_aspect 确保无论尺寸大小,任何纬度都不会压扁
p = figure(
tools="wheel_zoom,reset",
match_aspect=True,
background_fill_color='#440154'
)
p.grid.visible = False
p.hex_tile(
bins.q,
bins.r,
size=0.1,
line_color=None,
fill_color=color
)
show(p)
# 练习: 使用 hexbin 的 size 参数进行实验,并使用不同的数据作为输入
from bokeh.palettes import Viridis256
from bokeh.util.hex import hexbin
n = 50000
x = np.random.standard_normal(n)
y = np.random.standard_normal(n)
bins = hexbin(x, y, 0.2)
color = [Viridis256[int(i)] for i in bins.counts/max(bins.counts)*255]
p = figure(
tools="wheel_zoom,reset",
match_aspect=True,
background_fill_color='#440154'
)
p.grid.visible = False
p.hex_tile(
bins.q,
bins.r,
size=0.1,
line_color=None,
fill_color=color
)
show(p)
图像
另一个常见任务是显示图像,该图像可能表示热度图或某种传感器数据。
Bokeh 提供了两种显示图像的 glyph 方法:
image
可以与调色板一起使用,在绘图中显示带有颜色映射的二维数据image_rgba
可用于在绘图中显示原始RGBA像素数据
下面的第一个示例显示了如何使用二维数组和调色板调用 figure
N = 500
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
img = np.sin(xx)*np.cos(yy)
p = figure(x_range=(0, 10), y_range=(0, 10))
# 必须为 image 参数指定矢量格式的图像数据
p.image(
image=[img],
x=0,
y=0,
dw=10,
dh=10,
palette="Spectral11"
)
show(p)
调色板可以是任何颜色列表,也可以是已命名的内置调色板之一,可以在bokeh.palettes参考指南中看到。
尝试更改调色板或数组数据,然后重新运行上面的单元格。
N = 500
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
img = np.sin(xx)*np.cos(yy)
p = figure(x_range=(0, 10), y_range=(0, 10))
p.image(
image=[img],
x=0,
y=0,
dw=10,
dh=10,
palette="Plasma256"
)
show(p)
下一个示例介绍如何使用 image_rgba
方法显示原始RGBA数据(在NumPy的帮助下创建)。
from __future__ import division
import numpy as np
N = 20
img = np.empty((N,N), dtype=np.uint32)
# 使用数组视图分别设置每个RGBA通道
view = img.view(dtype=np.uint8).reshape((N, N, 4))
for i in range(N):
for j in range(N):
view[i, j, 0] = int(i/N*255) # red
view[i, j, 1] = 158 # green
view[i, j, 2] = int(j/N*255) # blue
view[i, j, 3] = 255 # alpha
# 使用 figure 创建一个新的绘图,使用固定的范围
p = figure(x_range=[0,10], y_range=[0,10])
# 添加一个 RGBA 图像渲染器
p.image_rgba(image=[img], x=[0], y=[0], dw=[10], dh=[10])
show(p)
尝试更改RGBA数据并重新运行上面的单元格。
from __future__ import division
import numpy as np
N = 20
img = np.empty((N,N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((N, N, 4))
for i in range(N):
for j in range(N):
view[i, j, 0] = int(i/N*255) # red
view[i, j, 1] = int(j/N*255) # green
view[i, j, 2] = 158 # blue
view[i, j, 3] = 255 # alpha
p = figure(x_range=[0,10], y_range=[0,10])
p.image_rgba(image=[img], x=[0], y=[0], dw=[10], dh=[10])
show(p)
其它种类的 Glyphs
Bokeh 支持许多其他种类的glyphs。
您可以单击下面的用户指南链接,查看如何使用 bokeh.plotting
接口创建使用这些 glyphs 的图。
在本教程的条形图和分类数据图一章中,我们将更广泛地使用条形图和矩形图来介绍各种条形图(例如堆叠和分组)。
# 练习:跟随用户指南中的示例,使用其他种类 glyph 绘图。
from math import pi
p = figure(
plot_width=400,
plot_height=400
)
p.oval(
x=[1, 2, 3],
y=[1, 2, 3],
width=0.2,
height=40,
color="#CAB2D6",
angle=pi/3,
height_units="screen"
)
show(p)
使用多种 glyphs 绘图
最后,应注意可以在同一个图形上组合使用多个 glyphs。 当在单个图形上多次调用 glyphs 方法时,glyphs 按调用顺序绘制,如下所示。
# 创建一些数据
x = [1, 2, 3, 4, 5]
y = [4, 8, 2, 6, 3]
# 使用 figure 创建一个新的图形
p = figure(
plot_width=400,
plot_height=400,
)
# 添加折线图和圆点图
p.line(x, y, line_width=2)
p.circle(x, y, fill_color="white", size=8)
show(p)
# 练习: 使用多种 glyphs创建自己的图形
x = [1, 2, 3, 4, 5]
y = [6, 7, 8, 7, 3]
p = figure(
plot_width=400,
plot_height=400
)
p.vbar(
x=x,
width=0.5,
bottom=0,
top=y,
)
p.circle(x, y, fill_color="white", size=8)
show(p)