你是否曾经创建过一个Jupyter笔记本,并希望能用一组不同的参数来生成该笔记本?如果是这样,你可能至少做过以下一件事。
- 编辑单元格中的变量并重新运行笔记本,根据需要保存一份副本
- 保存了一份笔记本的副本,也许还修改了代码,直接在.ipynb文件中编辑了数值,然后重新运行了笔记本
- 编写一些自定义代码,用从数据库或配置文件加载的数据来设置变量,然后重新运行笔记本。
事实证明,对于这个问题有一个很好的解决方案,它可以将交互式笔记本参数化,并与自动化作业很好地共存,它被称为papermill。
动机
许多笔记本作者使用的标准做法是在笔记本的顶部附近为全局变量指定一个单元。然后作者或笔记本的其他用户修改单元格中的值,并运行整个笔记本以获得不同的结果。为了保持输出,作者将手动下载另一种格式的笔记本或将其保存为不同的笔记本文件。但是,只使用笔记本服务器和这些手工方法很快就会变得混乱和难以追踪,更不用说容易出错了。哪个笔记本是你编辑的?Papermill有助于解决这个问题。在这篇文章中,我将介绍papermill和基本用法,走过一个参数化的例子,最后谈谈使用cron完全安排和自动执行笔记本的方法。
使用papermill,笔记本中的一个特殊单元被指定为参数。当papermill执行一个参数化的笔记本时,无论是通过命令行界面(CLI)还是使用Python API,参数被传入并在随后的单元中执行。这使得笔记本可以用不同的参数快速运行多次。然后,所执行的笔记本可以保存在各种地方,包括本地或云存储。
安装
要安装papermill,请使用pip。我建议使用virtualenv或conda的虚拟环境。我经常推荐使用pyenv来安装最新的Python版本,并用于创建virtualenv。但是使用你最熟悉的任何东西。
pip install papermill
如果你想使用各种输入和输出选项(比如亚马逊的s3
或微软的azure
,你可以安装所有的依赖项。我在这里就不多说了,但文档中涵盖了这些选项,你甚至可以扩展papermill,为笔记本的输入/输出(I/O)添加其他处理程序。
pip install papermill[all]
基本使用
大多数用户想用papermill做的第一件事是为笔记本设置参数。我做了一个简单的笔记本例子,你可以下载并跟着做。一旦你运行了Jupyter并打开了一个笔记本,你所需要做的就是给带有参数的单元格添加一个参数标签。
你如何在Jupyter笔记本中添加一个标签。
保存笔记本,现在你就可以用papermill来执行它了。对于笔记本的例子,使用CLI来运行笔记本,提供你自己的名字。
papermill -p name Matt papermill_example1.ipynb papermill_matt.ipynb
这个命令是告诉papermill执行输入的笔记本papermill_example1.ipynb
,并将输出写入papermill_matt.ipynb
,同时将参数name
设为值Matt
。如果你打开产生的笔记本,现在的内容将包括一个新的单元格,在parameters
-标记的单元格之后,有一个injected-parameters
的标记,像这样。
注入参数后的笔记本(有新的单元)。
你现在应该看到,你可以根据需要添加尽可能多的参数,从一个现有的笔记本中制作新的笔记本。把主笔记本(在我们的例子中,papermill_example1.ipynb
)看作是一个模板,你可以用它来快速注入参数,制作你想要的许多副本。
基本的API使用
你可能想用Python代码来获取或建立你的注入参数,因此也有一个Python API可以用来执行papermill。我们可以在Python脚本中实现与上面完全相同的结果(或者在笔记本中,它在那里也很好用--并且会动态地显示进度)。
import papermill as pm
name = "Matt"
res = pm.execute_notebook(
'papermill_example1.ipynb',
'papermill_{name}.ipynb',
parameters = dict(name=name)
)
{"version_major":2,"version_minor":0,"model_id":"cf8280b216094bf6a75a9536b6505051"}
更多的参数传递
到目前为止,我们只传递了一个参数,并且使用了 -p 选项。你可以用几种方式传递参数。
命令行
你可以用笔记本的例子运行这些,然后自己查看结果。首先,你可以从CLI中指定多个参数。即使一个参数在笔记本中还不存在,参数也可以传入并创建。在这种情况下,papermill将创建一个injected-parameters
单元,并在笔记本的顶部执行它。
这里有一个例子。
papermill -p name Matt -p level 5 -p factor 0.33 -p alive True papermill_example1.ipynb papermill_matt.ipynb
或者用长选项代替...
papermill --parameters name Matt --parameters level 5 --parameters factor 0.33 --parameters alive True papermill_example1.ipynb papermill_matt.ipynb
注意,-p
或--parameters
选项将尝试解析整数和浮点数,所以如果你想让它们被解释为字符串,你就使用-r
或--raw
选项来让所有的值以字符串的形式传入。
papermill -r name Matt -r level 5 -r factor 0.33 -r alive True papermill_example1.ipynb papermill_matt.ipynb
你也可以使用yaml来指定参数。这可以通过一个文件(-f
或--parameters_file
)、一个字符串(-y
或--parameters_yaml
)或一个base64编码的字符串(-b
或--parameters_base64
)传入。这允许你传入更复杂的数据,包括列表和字典。
papermill papermill_example1.ipynb papermill_matt.ipynb -y "
name: Matt
level: 5
factor: 0.33
alive: True
sizes:
- 1.0
- 2.5
- 3.7
params:
x: 3
y: 4"
你可以非常容易地对字符串进行base64编码。(在你的Mac或Linux的shell中或Windows的WSL中,在笔记本文件所在的目录中运行这个程序)。
echo "
name: Matt
level: 5
factor: 0.33
alive: True
sizes:
- 1.0
- 2.5
- 3.7
params:
x: 3
y: 4" > params.yaml
现在你可以运行文件版本。
papermill papermill_example1.ipynb papermill_matt.ipynb -f params.yaml
或者base64版本
PARAMS=$(cat params.yaml| base64) # makes the base64 version of the yaml file
papermill papermill_example1.ipynb papermill_matt.ipynb -b $PARAMS
无论哪种方式,你都应该明白,你可以从命令行中把复杂的数据传入你的笔记本,也可以通过API。这些例子都使用本地文件系统进行笔记本的输入和输出,但请注意,你可以从亚马逊s3
、Azure、谷歌云存储或网络服务器上读写笔记本。
检查笔记本
你也可以从CLI检查一个笔记本的可用参数。
$ papermill --help-notebook papermill_example1.ipynb
Usage: papermill [OPTIONS] NOTEBOOK_PATH [OUTPUT_PATH]
Parameters inferred for notebook 'papermill_example1.ipynb':
name: Unknown type (default "Joe")
或者使用Python API。
pm.inspect_notebook('papermill_example1.ipynb')
{'name': {'name': 'name',
'inferred_type_name': 'None',
'default': '"Joe"',
'help': ''}}
执行一个完整的工作流
papermill的一个典型的工作流程是有一个参数化的笔记本,用多个值运行它,然后把产生的笔记本转换成另一种格式,用于审查或报告。让我们举个例子来说明如何设置。
首先,我们有一个参数化的笔记本,它使用雅虎金融的API来获取股票价格,并以股票的历史最高价来绘制数据(或者至少是过去两年的最高价,因为我在这一点上只获取了这么多数据)。
如果你想运行这个例子,你将需要确保你安装了yfinance
API以及matplotlib
。如果需要的话,你可以用pip安装这两者。
我们可以使用papermill CLI来检查参数。
$ papermill --help-notebook papermill_example2.ipynb
Usage: papermill [OPTIONS] NOTEBOOK_PATH [OUTPUT_PATH]
Parameters inferred for notebook 'papermill_example2.ipynb':
symbol: Unknown type (default 'AAPL')
我们将用几个符号来运行这个笔记本。我选择使用一个shell脚本,这样我就可以通过一个预定的cron job来运行它。 如果需要,这也可以用一个简单的Python脚本来完成。然而,如果你使用的是一个虚拟环境,你可能最终需要一个脚本来确保虚拟环境被正确加载。在这种情况下,使用shell脚本来完成整个过程可能更容易。
我也将使用 jupyter nbconvert
(或者你可以以jupyter-nbconvert
)命令将笔记本转换为html文件,以便通过网络浏览器查看。就像papermill一样,nbconvert可以通过命令行或使用Python API获得。
自动化脚本
#!/bin/bash
set -eux
# activate our virtualenv (this was created using pyenv-virtualenv, yours will be elsewhere)
source /Users/mcw/.pyenv/versions/3.8.6/envs/pandas/bin/activate
# get to the script directory if running via cron
cd $(dirname "${BASH_SOURCE[0]}")
for S in AAPL MSFT GOOG FB
do
papermill -p symbol $S papermill_example2.ipynb papermill_${S}.ipynb
jupyter-nbconvert --no-input --to html papermill_${S}.ipynb
done
你可以从你的shell中运行这个命令(在调整了激活虚拟环境的那一行以反映你自己的设置之后)。你也可以很容易地在cron中安排它定期运行。例如,你可以像这样在每个工作日下午4点运行这个报告(用你自己的路径)。
00 16 * * mon-fri /Users/mcw/projects/python_blogposts/tools/run_papermill.sh
扩展该例子
只要再有一点创造力(和在nbconvert上的软件配置),你就可以把笔记本输出为PDF或其他格式,通过电子邮件发送,或上传到服务器上,让漂亮的报告每天都能更新。
请注意,每个符号的笔记本都保存在本地磁盘上。如果需要调试或进一步的工作,它们可以在Jupyter服务器中打开并轻松地重新执行。要知道,如果你有一个自动作业在运行,每次运行时笔记本都会被替换。理想情况下,你想在你的主模板笔记本上工作,然后用自动化为每个符号生成新版本。
另一个提示是,papermill可以读写标准输入和输出。这意味着,如果你有其他工具把笔记本文件作为输入,你就不必把文件写到磁盘上。例如,在我们上面的shell脚本中,我们可以不写出每个符号的单个笔记本文件,而是在我们的循环中做以下工作。
papermill -p symbol $S papermill_example2.ipynb | jupyter-nbconvert --stdin --no-input --to html --output report_${S}.html
注意,如果你这样做,你需要打开主笔记本 (papermill_example2.ipynb
) 并编辑你的参数来调试问题。但是,如果你需要节省磁盘空间,并且不需要对每个笔记本进行单独调试的能力,这也许是比较可取的。
总结
Papermill是一个有用的库,用于参数化和执行Jupyter笔记本。你可以用它来自动执行你的笔记本和你能想到的任何参数集。 随后,使用nbconvert对笔记本进行转换,以提供可读和有用的笔记本版本。
笔记本自动化可以做的事情还有很多,但从papermill开始作为执行和参数化笔记本的工具是一个很好的平台。
The postParameterizing and automating Jupyter notebooks with papermillappeared first onwrighters.io.