代码、结果和笔记集于一个文档
Jupyter 笔记本是由单元格组成的文档。代码单元格里是可运行的 Python,输出(数字、表格、图表)就显示在它下面。Markdown 单元格里是带格式的文字,用于说明、公式和标题。后台进程内核(kernel)负责运行代码,并在单元格之间记住所有变量。笔记本非常适合探索数据和解释分析,因此在量化研究中随处可见。
# Inside your project's virtual environment
python -m pip install jupyterlab
# Start it; a browser tab opens
jupyter lab
# Keyboard shortcuts you will use all day
# Shift+Enter run the cell and move on
# Esc then A/B insert a cell above/below
# Esc then M/Y turn a cell into markdown/code内核会记住一切,这正是风险所在
单元格可以按任意顺序、任意次数运行,内核会保留每个变量的最新值。因此,笔记本在屏幕上看起来正确,却可能依赖于一个已被删除的单元格或乱序执行,别人(甚至明天的你)都无法复现。每个代码单元格旁的执行计数([3]、[7])显示真实的运行顺序。在信任或分享笔记本之前,请用重启内核并运行全部单元格:如果失败或数字改变,说明这个笔记本不可复现。
# Cell [1]
fee = 0.001
# Cell [2]
cost = 10000 * fee
print(cost) # 10.0
# Cell [3]: you edit this to fee = 0.002 but never re-run cell [2]
fee = 0.002
print(cost) # still 10.0: the notebook now says fee=0.002 but shows the old cost笔记本其实就是 JSON
.ipynb 文件是一个带有 cells 列表的 JSON 文档。每个单元格有 cell_type("code"、"markdown" 或 "raw")和 source(行的列表或一个字符串);代码单元格还保存 outputs 和 execution_count。因为它就是普通 JSON,所以可以用 json 模块读写笔记本,工具正是这样把它们转成脚本或清除输出的。这也解释了为什么笔记本会让 Git 差异变得嘈杂:每次运行,输出和计数都会变化。
import json
notebook = {
"nbformat": 4, "nbformat_minor": 5, "metadata": {},
"cells": [
{"cell_type": "markdown", "metadata": {}, "source": ["# Fee study\n", "Does the fee matter?"]},
{"cell_type": "code", "metadata": {}, "execution_count": 2,
"source": ["fee = 0.001\n", "print(10000 * fee)"],
"outputs": [{"output_type": "stream", "name": "stdout", "text": ["10.0\n"]}]},
],
}
# What the file looks like, and how to walk it
print(len(json.dumps(notebook, indent=1).splitlines()) > 10) # True
for cell in notebook["cells"]:
print(cell["cell_type"], "".join(cell["source"]).splitlines()[0])
# markdown # Fee study
# code fee = 0.001让笔记本整洁可靠的好习惯
保持笔记本简短且线性:自上而下,导入和设置放在第一个单元格,每个想法用一个 Markdown 标题成节。把可复用的逻辑移到普通 .py 模块(见“包”)的函数里并导入,便于测试;笔记本只负责调用和绘图。每次提交前都重启并运行全部。使用 Git 时先清除输出(nbstripout 之类的工具可以自动完成),否则会提交几兆字节的图表和难以阅读的差异。绝不要把 API 密钥留在单元格或输出里。
# Keep notebooks tidy under Git
python -m pip install nbstripout
nbstripout --install # strips outputs automatically on every commit
# Run a notebook top to bottom from the terminal (a reproducibility check)
jupyter nbconvert --to notebook --execute study.ipynb --output checked.ipynb
# Turn a notebook into a plain script
jupyter nbconvert --to script study.ipynb.py 文件中的函数并配上测试。笔记本用来提问,模块用来存放你可以依赖的答案。本实验把笔记本的 JSON 当作普通字典来处理,此类工具正是这样编写的。- 单元格可乱序运行且内核会记住状态:信任或分享前先重启并运行全部。
.ipynb是 JSON:cells,每个有cell_type、source,代码单元格还有outputs和execution_count。- 逻辑放进模块,提交前清除输出,不要放入机密。
下一节:可复现的研究工作流,把随机种子、环境、版本和数据检查结合起来,让任何人都能重建结果。
实验:评分内容
- 用
code_cells提取笔记本中的代码25 分 - 用
count_cells统计各类单元格数量25 分 - 用
clear_outputs在提交前清除输出25 分 - 用
to_script把笔记本转换为脚本25 分