[译] 利用 Python 中的 Bokeh 实现数据可视化,第一部分:入门

提高你的可视化游数据html

若是没有有效的方法来传达结果,那么再复杂的统计分析也毫无心义。这一点我在最近的研究项目中深有体会,咱们使用数据科学来提升建筑能效。在过去的几个月里,我团队成员中的一我的一直在致力于研究一种叫作 wavelet transforms,用于分析时间序列频率成分的技术。该方法取得了积极的效果,但她在解释过程当中遇到了困难,所幸的是,她没有迷失在技术细节中。前端

她很愤怒,问我可否用视觉表达来讲明这种变换。我使用了叫作 gganimate 的 R 包,在几分钟以内制做了一个简单的动画,展现了该方法是如何转换时间序列的。如今,个人团队成员能够用这个让人直观地了解技术是如何工做的东西来取代费劲的语言描述。个人结论是,咱们能够作最严格的分析,但在一天结束时,全部人都想看到的是一个 gif!虽说这话是开玩笑,但它蕴含着一个道理:不能清楚地表达结果,就会对结果产生影响,而数据可视化一般是展现分析结果的最佳方法。python

可用于数据科学的资源正在迅速增长,在可视化领域中尤其明显,彷佛每周都有一种新的尝试。随着这些技术的进步,它们逐渐出现了一个共同的趋势:增长交互性。人们喜欢在静态图中查看数据,但他们更喜欢的是使用数据,并利用这些数据来查看参数的变化对结果的影响。在个人研究中,有一份报告是用来告诉业主经过改变他们的空调使用时间能够节省下多少度电,但若是给他们一个能够交互的表,他们就能够本身选择不一样的时间表,来观察不用时间是如何影响用电的,这种方式更加有效。最近,受交互式绘图趋势的启发,以及对不断学习新工具的渴望,我一直在学习使用一个叫作 Bokeh 的 Python 库。我为个人研究项目构建的仪表盘中显示了 Bokeh 交互功能的一个示例:android

尽管我没法共享这个项目的整个代码,但我能够经过使用公开可用数据构建彻底交互的 Bokeh 应用程序的示例。本系列文章将介绍使用 Bokeh 建立应用程序的整个过程。对于第一篇文章,咱们将介绍 Bokeh 的基本元素,咱们将在之后的文章中对其进行构建,在本系列文章中,咱们将使用 nycflights13 数据集,该数据集有 2013 年以来超过 30 万次航班的记录。咱们首先将重点放在可视化单个变量上,在这种状况下,航班的延迟到达以分钟为单位,咱们将从构造一个基本的柱状图开始,这是显示一个连续变量的扩展和位置的经典方法。完整的代码能够在 GitHub 查看,第一个 Jupyter notebook 能够在这里看到。这篇文章关注的是视觉效果,因此我鼓励任何人查看代码,若是他们想看到无聊但又必不可少数据清洗和格式化的步骤!ios

Bokeh 基础

Bokeh 的主要概念是一次创建一个图层。咱们首先建立一个图,而后向图中添加名为 glyphs 的元素。(对于那些使用 ggplot 的人来讲,glyphs 的概念与地理符号的想法本质上是同样的,他们一次添加到一个“图层”中。)根据所需的用途,glyphs 能够呈现多种形状:圆形、线条、补丁、条形、弧形等。让咱们用正方形和圆形制做一个基本的图来讲明 glyphs 的概念。首先,咱们使用 figure 方法绘制一个图,而后经过调用适当的方法传入数据,将咱们的 glyphs 添加到绘图中。最后,咱们展现绘图(我使用的是 Jupyter Notebook,若是你使用时调用的是 output_notebook,就会看到对应的绘图)。git

# bokeh 基础
from bokeh.plotting import figure
from bokeh.io import show, output_notebook

# 建立带标签的空白图
p = figure(plot_width = 600, plot_height = 600, 
           title = 'Example Glyphs',
           x_axis_label = 'X', y_axis_label = 'Y')

# 示例数据
squares_x = [1, 3, 4, 5, 8]
squares_y = [8, 7, 3, 1, 10]
circles_x = [9, 12, 4, 3, 15]
circles_y = [8, 4, 11, 6, 10]

# 添加方形 glyph
p.square(squares_x, squares_y, size = 12, color = 'navy', alpha = 0.6)
# 添加圆形 glyph
p.circle(circles_x, circles_y, size = 12, color = 'red')

# 设置为在笔记本中输出情节
output_notebook()
# 显示绘图
show(p)
复制代码

这就造成了下面略显平淡的绘图:github

尽管在任何绘制图库中,咱们均可以很容易地制做这个图表,但咱们能够免费获取一些工具,其中包含位于右侧的 Bokeh 绘图,包括 panning,缩放和绘图保存功能。这些工具是可配置的,当咱们想研究咱们的数据时,这些工具会派上用场。web

咱们如今开始展现咱们的航班延迟数据。在跳转到图形以前,咱们应该加载数据并对其进行简短的检查(粗体 为输出代码):后端

# 将 CSV 中的数据读入
flights = pd.read_csv('../data/flights.csv', index_col=0)

# 兴趣栏的统计数据汇总
flights['arr_delay'].describe()

count    327346.000000
mean          6.895377
std          44.633292
min         -86.000000
25%         -17.000000
50%          -5.000000
75%          14.000000
max        1272.000000
复制代码

摘要统计数据为咱们做出决策提供了信息:咱们有 32七、346 次航班,最小延迟事件为 -86 分钟,最大延迟事件为 1272 分钟,使人震惊的 21 小时!75% 的分位数只有 14 分钟,因此咱们能够假设 1000 分钟以上的数字多是异常值(这并不意味着它们是非法的,只是极端的)。我会集中讨论 -60 到 120 分钟的延迟柱状图。分布式

柱状图是单个变量初始可视化的常见选择,由于它显示了分布式数据。x 位置是将变量分组成成为 bin 的间隔的值,每一个条形的高度表示每一个间隔数据点的计数(数目)。在咱们的例子中,x 位置将表明以分钟为单位的延迟到达,高度是对应的 bin 中的航班数。Bokeh 没有内置的柱状图,但咱们可使用 quad glyph 来指定每一个条形的底部、上、下、和右边距。

要建立条形图的数据,咱们要使用 numpy[histogram](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.histogram.html)function,它计算每一个指定 bin 数据点的数值。咱们使用 5 分钟的长度做为函数将计算航班数在每五分钟所花费的时间延误。在生成数据以后,咱们将其放入一个 pandas dataframe 来将全部的数据保存在一个对象中。这里的代码对于理解 Bokeh 并非很重要,但鉴于 Numpy 和 pandas 在数据科学中的流行度,因此它仍是有些用处的。

"""Bins will be five minutes in width, so the number of bins is (length of interval / 5). Limit delays to [-60, +120] minutes using the range."""

arr_hist, edges = np.histogram(flights['arr_delay'], 
                               bins = int(180/5), 
                               range = [-60, 120])

# 将信息放入 dataframe
delays = pd.DataFrame({'arr_delay': arr_hist, 
                       'left': edges[:-1], 
                       'right': edges[1:]})
复制代码

咱们的数据看起来像这样:

flights 列是从 leftright 的每一个延迟间隔内飞行次数的计数。在这里,咱们能够生成一个新的 Bokeh 图,并添加一个指定适当参数的 quad glpyh:

# 建立空白绘图
p = figure(plot_height = 600, plot_width = 600, 
           title = 'Histogram of Arrival Delays',
          x_axis_label = 'Delay (min)]', 
           y_axis_label = 'Number of Flights')

# 添加一个 quad glphy
p.quad(bottom=0, top=delays['flights'], 
       left=delays['left'], right=delays['right'], 
       fill_color='red', line_color='black')

# 显示绘图
show(p)
复制代码

生成此图的大部分工做都是在数据格式化过程当中进行的,这在数据科学中并不常见!从咱们的绘图中能够看出,延迟到达几乎是正态分布的,右侧有一个轻微的正斜度或重尾巴

有更简单的方法能够在 Python 中建立柱状图,也可使用几行 [matplotlib](https://en.wikipedia.org/wiki/Matplotlib) 来获取相同的结果。可是,Bokeh 绘图所带来的的开发的好处在于,它能够提供将数据交互轻松地添加到图形中的工具和方法。

添加交互性

咱们将在本系列中讨论的第一类交互是被动交互。这些是拥护能够采起的不改变显示数据的操做。它们被称为 inspectors,由于他们容许用户查看更详细的“调查”数据。有用的 inspector 是当用户鼠标在数据点上移动并调用 Bokeh 中的悬停工具时,会出现工具提示。

基础的悬停工具提示

为了添加工具提示,咱们须要将数据源从 dataframe 中更改成来自 ColumnDataSource,Bokeh 中的一个关键概念。这是一个专门用于绘图的对象,它包含数据以及方法和属性。ColumnDataSource 容许咱们在图中添加注解和交互,也能够从 pandas dataframe 中进行构建。真实数据被保存在字典中,能够经过 ColumnDataSource 的 data 属性访问。这里,咱们从数据源进行建立源,并查看数据字典中与 dataframe 列对应的键。

# 导入 ColumnDataSource 类
from bokeh.models import ColumnDataSource

# 将 dataframe 转换为 列数据源
src = ColumnDataSource(delays)
src.data.keys()

dict_keys(['flights', 'left', 'right', 'index'])
复制代码

咱们使用 CloumDataSource 添加 glyphs 时,咱们将 CloumnDataSource 做为 source 参数传入,并使用字符串引用列名:

# 此次添加一个带有源的 quad glyph
p.quad(source = src, bottom=0, top='flights', 
       left='left', right='right', 
       fill_color='red', line_color='black')
复制代码

请注意,代码如何引用特定的数据列,好比 ‘flights’、‘left’ 和 ‘right’,而不是像之前那样使用 df['column'] 格式。

Bokeh 中的 HoverTool

一开始,HoverTool 的语法看上去会有些复杂,但通过实践后,就会发现它们很容易建立。咱们将 HoverTool 实例做为 tooltips 做为 Python 元组传递给它,其中第一个元素是数据的标签,第二个元素引出咱们要高亮显示的特定数据。咱们可使用 ‘$’ 引用图中任何属性,例如 x 或 y 的位置,也可使用 ‘@’ 引用源中特定字段。这听起来可能有点使人困惑,因此这里有一个 HoverTool 的例子,咱们在这两方面均可以这么作:

# 使用 @ 引用咱们本身的数据字段
# 使用 $ 在图上的位置悬停工具
h = HoverTool(tooltips = [('Delay Interval Left ', '@left'),
                          ('(x,y)', '($x, $y)')])
复制代码

这里,咱们使用 ‘@’ 引用 ColumnDataSource(它对应于原始 dataframe 的 ‘left’ 列)中的 left 数据字段,并使用 ‘$’ 引用光标的 (x,y) 位置。结果以下:

显示不一样数据引用的悬停工具提示

(x,y) 位置上是鼠标的位置,对咱们的柱状图没有太大的帮助,由于咱们要找到给定条形中对应于条形顶部的飞行术。为了修复这个问题,咱们将要修改咱们的工具提示实例来引用正确的列。格式化工具提示中的数据显示可能会让人沮丧,所以我一般在 dataframe 中使用正确的格式建立另外一列。例如,若是我但愿个人工具提示显示给定条的整个隔间,我会在数据框中建立一个格式化列:

# 添加一个列,显示每一个间隔的范围
delays['f_interval'] = ['%d to %d minutes' % (left, right) for left, right in zip(delays['left'], delays['right'])]
复制代码

而后,我将 dataframe 转换为 CloumnDataSource,并在 HoverTool 调用中访问该列。下面的代码使用引用两个格式化列的悬停工具建立绘图,把那个将该工具添加到绘图中。

# 建立一个空白绘图
p = figure(plot_height = 600, plot_width = 600, 
           title = 'Histogram of Arrival Delays',
          x_axis_label = 'Delay (min)]', 
           y_axis_label = 'Number of Flights')

# 此次,添加带有源的 quad glyph
p.quad(bottom=0, top='flights', left='left', right='right', source=src,
       fill_color='red', line_color='black', fill_alpha = 0.75,
       hover_fill_alpha = 1.0, hover_fill_color = 'navy')

# 添加引用格式化列的悬停工具
hover = HoverTool(tooltips = [('Delay', '@f_interval'),
                             ('Num of Flights', '@f_flights')])

# 绘图样式
p = style(p)

# 将悬停工具添加到图中
p.add_tools(hover)

# 显示绘图
show(p)
复制代码

在 Bokeh 样式中,咱们以添加元素至原始的图中来将元素添加到表中。请注意,在 p.quad glyph 调用中,有几个额外的参数 hover_fill_alphahover_fill_color,当咱们的鼠标移动到条图形时,这些参数会改变 glyph 的样式。我还添加了 style 函数(可在笔记中查看相关代码)。审美过程很无聊,因此一般我会写一个应用于任何绘图的函数。当我使用样式时,我会保持简单并专一于标签的可读性。绘图的主要目的是显示数据,添加没必要要的元素只会下降绘图的可用性!最后的绘图以下所示:

当咱们的鼠标滑过不一样的词条时,会获得该词条精确的统计数据,它表示间隔以及在该间隔内飞行的次数。若是对绘图比较满意,能够将其保存到 html 文件中进行共享:

# 导入保存函数
from bokeh.io import output_file

# 指定输出文件并保存
output_file('hist.html')
show(p)
复制代码

展望与总结

为了获取 Bokeh 的工做流程,我制做了不少次绘图,因此若是这看起来有不少东西要学的时候,不要担忧。在本系列教程中,咱们将获得更多的练习!虽然Bokeh 看起来彷佛有不少工做要作,可是当咱们想要将咱们的视觉效果扩展到简单的静态图像以外的时候,它的好处就不言而喻了。一旦咱们有了基本的图,咱们就能够经过增长更多的元素来提升视觉效果。例如,若是咱们想查看航空公司的延迟到达,咱们能够制做一个交互式图,让用户选择和比较航空公司。咱们将把主动交互(那些更改显示数据的交互)留到下一篇文章中,但下面是咱们目前能够作的事情:

主动交互须要编写更多的脚本,但这给了咱们可使用 Python 的机会!(若是有人想在下一篇文章以前看一下绘图的代码能够在这里进行查看。)

在本系列文章中,我想强调的是,Boken 或者任何一个库工具永远都不会是知足全部绘图需求的一站式解决工具。Bokeh 容许用户研究绘图,但对于其余应用,像简单的探索性数据分析matplotlib 这样的轻量级库可能会更高效。本系列旨在为你提供绘图工具的另外一种选择,这须要更加需求来进行抉择。你知道的库越多,就越能高效地使用可视化工具完成任务。

我一直以来都很是欢迎那些具备建设性的批评和反馈。大家能够在 Twitter @koehrsen_will 上联系到我。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索