使用交互组件(ipywidgets)“盘活”Jupyter Notebook(上)

2019年05月09日 由 sunlei 发表 916575 0


扩展Jupyter的用户界面

传统上,每次需要修改笔记本单元格的输出时,都需要更改代码并重新运行受影响的单元格。这可能很繁琐、低效甚至容易出错,对于非技术用户来说,甚至是不切实际的。这就是ipywidgets发挥作用的地方:它们可以嵌入到笔记本中,并提供一个用户友好的界面来收集用户输入并查看更改对数据/结果的影响,而不必与代码交互;你的笔记本可以从静态文档转换为动态仪表盘——非常适合显示你的数据故事!

范围:ipywidgets上的资源有限,很少有教程是不完整的,或者只关注交互功能/装饰器。这是一个完整的教程,介绍如何完全控制小部件来创建强大的仪表盘。我们将从基础开始:添加一个小部件并解释事件如何工作,然后逐步开发一个仪表盘。我将一步一步地指导你,以我们正在进行的示例为基础。

什么是小部件?

如果你曾经创建过图形用户界面(GUI),那么你已经知道小部件是什么。但让我们快速定义一下:

小部件是GUI元素,如按钮、下拉菜单或文本框,它驻留在浏览器中,允许我们通过响应事件和调用指定的处理程序来控制代码和数据。

可以组装和定制这些GUI元素来创建复杂的仪表盘。

[caption id="attachment_40066" align="aligncenter" width="1098"] 演示:一些最流行的小部件[/caption]

在本文中,我们将看到其中一些方法的实际应用。

准备好了吗?

1、开始

要开始使用这个库,我们需要安装ipywidgets扩展。如果使用conda,我们在终端输入这个命令:
conda install -c conda-forge ipywidgets

对于pip,这将是一个两步的过程:1、安装和2、启用:
pip install ipywidgetsjupyter nbextension enable --py widgetsnbextension

添加小部件

为了在笔记本中加入小部件,我们必须导入模块,如下图所示:
import ipywidgets as widgets

要添加滑块,我们可以定义最小值和最大值、间隔大小(步骤)、说明和初始值:
widgets.IntSlider(
min=0,
max=10,
step=1,
description='Slider:',
value=3
)

[caption id="attachment_40067" align="aligncenter" width="920"] 演示:滑块[/caption]

显示

函数的作用是:在输入单元格中呈现小部件对象。

首先导入:
from IPython.display import display

然后在display()函数中传递小部件作为参数:
slider = widgets.IntSlider()
display(slider)

获取/设置它的值

要读取小部件的值,我们将查询它的value属性。同样,我们可以设置小部件的值:

[caption id="attachment_40068" align="aligncenter" width="1020"] 演示:值[/caption]

连接两个小部件

我们可以使用jslink()函数同步两个小部件的值。
slider = widgets.IntSlider()
text = widgets.IntText()
display(slider, text)widgets.jslink((slider, 'value'), (text, 'value'))

[caption id="attachment_40069" align="aligncenter" width="920"] 演示:链接[/caption]

小部件列表

有关小部件的完整列表,你可以查看文档,或运行以下命令:
print(dir(widgets))

2、处理小部件事件

小部件可以响应事件,这些事件在用户与它们交互时引发。一个简单的例子是点击一个按钮——我们期待一个动作发生。

让我们看看这是怎么工作的…

根据其特定的特性,每个小部件公开不同的事件。每次触发事件时都将执行事件处理程序。

事件处理程序是响应事件的回调函数,它异步操作并处理接收到的输入。

这里我们将创建一个名为btn的简单按钮。单击按钮时调用on_click方法。

我们的事件处理程序btn_eventhandler将打印一条带有按钮标题的短消息——注意,处理程序的输入参数obj是按钮对象本身,它允许我们访问它的属性。

要将事件与处理程序绑定,我们将后者分配给按钮的on_click方法。
btn = widgets.Button(description='Medium')
display(btn)def btn_eventhandler(obj):
print('Hello from the {} button!'.format(obj.description))btn.on_click(btn_eventhandler)

[caption id="attachment_40070" align="aligncenter" width="920"] 演示:按钮事件处理程序[/caption]

下一节我们将很好地了解到,输出与按钮本身显示在同一个单元格中。所以,让我们继续看看如何为我们的笔记本增加更多的灵活性!

3、控制部件的输出

在本节中,我们将探索如何使用小部件来控制dataframe。我选择的样本数据集是“前往伦敦的国际游客数量”(Number of International Visitors to London),它显示了伦敦游客在不同年份、不同季度、不同目的、不同持续时间、不同模式和不同国家的夜晚、访问次数和消费情况。

首先,我们将获取数据并将其加载到一个dataframe中:
import pandas as pd
import numpy as npurl = "https://data.london.gov.uk/download/number-international-visitors-london/b1e0f953-4c8a-4b45-95f5-e0d143d5641e/international-visitors-london-raw.csv"df_london = pd.read_csv(url)

[caption id="attachment_40071" align="aligncenter" width="920"] df_london.样本[/caption]

假设我们想按年过滤数据帧。我们首先定义一个下拉列表,并用唯一的年份值列表填充它。

为了做到这一点,我们将创建一个通用函数,unique-sorted-values-plus-all,它将找到唯一的值,对它们进行排序,然后在开始时添加all项,这样用户就可以删除过滤器。
ALL = 'ALL'def unique_sorted_values_plus_ALL(array):
unique = array.unique().tolist()
unique.sort()
unique.insert(0, ALL)
return unique

现在我们将初始化下拉框:
dropdown_year = widgets.Dropdown(options =    unique_sorted_values_plus_ALL(df_london.year))

下拉菜单小部件公开了observer方法,该方法接受一个函数,当下拉菜单的值发生更改时将调用该函数。因此,我们接下来将创建观察者处理程序来根据所选的值过滤数据aframe——注意,处理程序的输入参数change包含有关发生的更改的信息,这些更改允许我们访问新值(change.new)。

如果新值是所有我们删除过滤器,否则我们应用它:
def dropdown_year_eventhandler(change):
if (change.new == ALL):
display(df_london)
else:
display(df_london[df_london.year == change.new])

然后我们将处理程序绑定到下拉列表:
dropdown_year.observe(dropdown_year_eventhandler, names='value')

[caption id="attachment_40072" align="aligncenter" width="920"] 使用下拉列表筛选数据帧[/caption]

到目前为止还不错,但是所有查询的输出都在这个非常相同的单元格中累积;也就是说,如果我们从下拉列表中选择一个新的年份,新的数据框将呈现在第一个单元格的下面,在同一个单元格上。

不过,理想的行为是每次刷新数据帧的内容。

捕获小部件输出

解决方法是在一种特殊的小部件(即输出)中捕获单元输出,然后将其显示在另一个单元中。

我们将稍微调整代码以:

  • 创建输出的新实例


output_year = widgets.Output()


  • 调用事件处理程序中的clear_output方法,在每次迭代中清除先前的选择,并在with块中捕获数据帧的输出。


def dropdown_year_eventhandler(change):
output_year.clear_output()
with output_year:
display(df_london[df_london.year == change.new])

然后我们将在一个新的单元格中显示输出:
display(output_year)

这就是它的工作原理:

[caption id="attachment_40073" align="aligncenter" width="1018"] 演示:捕获新单元格中的输出[/caption]

正如你所看到的,输出在一个新的单元格中呈现,过滤工作正常!

好了,今天先学习到这里,剩下的部分我们下次继续~
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
写评论取消
回复取消