Jupyter 风格指南#
文档中的 Notebook 应遵循这些指南。pymc-examples 中的所有 Notebook 必须严格遵守此指南,对于 pymc 上的 Notebook,风格可以更宽松,因为并非所有内容都可用。
文档网站由 Sphinx 生成,Sphinx 使用 MYST 和 MYST-NB 来解析 Notebook。
提示
有一个关于 为 PyMC 示例库贡献内容 的网络研讨会
模板 Notebook#
有一个 模板 Jupyter Notebook 可用于新的 Notebook。
通用指南#
在可以使用完整单词时,请勿使用缩写或首字母缩略词。例如,写“random variables”而不是“RVs”。
解释每个步骤背后的理由。
注明引用的文本或代码的出处,并链接到相关参考文献。
保持 Notebook 短小精悍:针对初级或中级用户的内容,Notebook 保持在 20/30 个单元格;高级水平的 Notebook 可以更长。
MyST 指南#
使用 MyST 可以利用 Notebook 中 Markdown 单元格的所有 Sphinx 功能。所有 Markdown 都应是有效的 MyST(请注意,MyST 是 recommonmark 的超集)。本指南不广泛地教授或涵盖 MyST,仅提供一些主观的指南。
永远不要使用 URL 链接来引用其他 Notebook、PyMC 文档或其他 Python 库文档。当链接到其他 Notebook 时,始终使用指向 第一个单元格 中目标的
ref
类型交叉引用。注意
在版本化文档中使用 URL 链接会破坏自引用!同时,它们也不如 Sphinx 交叉引用那么健壮。
交叉引用示例
引用当前项目内的目标
即,pymc-examples 中的 Notebook 引用 pymc-examples 中的其他 Notebook。
模式
{ref}`explicit text <anchor_id>`
示例来源
{ref}`Kronecker product <GP-Kron>`
渲染示例:Kronecker 乘积
引用其他项目的目标
这里“其他项目”指的是任何独立于当前项目构建的 Sphinx 文档站点。因此,这包括从 PyMC 文档链接到 pymc-examples Notebook,反之亦然,或者链接到其他库,如 ArviZ、NumPy、Matplotlib 等。
模式
{ref}`explicit text <key:anchor_id>`
示例来源
{ref}`how to use InferenceData <arviz:working_with_InferenceData>`
渲染示例:如何使用 InferenceData
其中模式中的
key
(示例中的arviz
)是在conf.py
的intersphinx_mapping
变量中定义的键之一,如arviz
、numpy
、mpl
... 对于主 PyMC 仓库,它位于docs/source/conf.py
中,对于 pymc-examples,它位于examples/conf.py
中。要识别要使用的
anchor_id
,您需要查看文档的源代码,或使用 sphobjinv。引用 Python 对象
模式
{type}`import.path` # to show full import path {type}`~import.path` # to show only object name
其中 type 是函数的 func、方法的 meth、类的 class、属性的 prop 等。
示例来源
{class}`~pymc.gp.HSGP`
渲染示例:
HSGP
如果单元格的输出(甚至代码和输出)对于理解 Notebook 不是必需的,或者它非常长并且会中断阅读流程,请考虑使用 切换按钮 隐藏它
考虑使用 Markdown 图形 为 Notebook 中使用的图像添加标题。
尽可能使用术语表。如果您使用术语表中定义的术语,请在首次以重要方式出现该术语时链接到它。使用 此语法 添加术语引用。链接到术语表源,应在其中添加新术语。
变量名#
最重要的是,在 Notebook 中保持变量名的一致性。对于同一变量使用多个名称的 Notebook 将不会被合并。
尽可能使用有意义的变量名。我们的用户来自不同的背景,并非所有人都熟悉相同的命名约定。
也请注释维度。Notebook 是发布供阅读的,因此即使形状是从输入派生的,或者您不喜欢使用命名维度并且在您的个人代码中不使用它们,Notebook 也必须使用维度,即使只是注释而不是设置形状。这使代码更易于理解,尤其是对于新手而言。
有时使用希腊字母来表示变量是有意义的,例如在编写方程式时,因为这使它们更易于阅读。在这种情况下,使用 LaTeX 插入希腊字母,例如
$\theta$
,而不是使用 Unicode,例如θ
。如果您需要在代码中使用希腊字母变量名,请拼写出来,而不是使用 Unicode。例如,
theta
,而不是θ
。当使用非有意义的名称(如单个字母)时,在首次引入它们的方程式下方添加带有 1-2 句话描述每个变量的要点。
选择变量名有时可能很困难、乏味或令人恼火。如果以下下拉菜单对您有所帮助,您可以专注于编写实际内容
变量名建议
模型和采样结果
对采样结果使用
idata
,始终包含 InferenceData 类型的变量。将 inferenceData 组存储为变量,以简化操作采样结果的代码的编写和阅读。使用下划线分隔的 3-5 个单词的缩写或组名。
abbrebiation
/group_name
的一些示例:post
/posterior
、const
/constant_data
、post_pred
/posterior_predictive
或obs_data
/observed_data
对于统计信息和诊断,使用 ArviZ 函数名作为变量名:
ess = az.ess(...)
,loo = az.loo(...)
如果 Notebook 中有多个模型,请为每个模型分配一个前缀,并在整个 Notebook 中使用它来标识哪些变量映射到每个模型。以著名的八所学校为例,使用
centered
和non_centered
模型来比较参数化,使用centered_model
(pm.Model 对象)、centered_idata
、centered_post
、centered_ess
... 和non_centered_model
、non_centered_idata
...
维度和随机变量名
使用单数维度名称,遵循 ArviZ
chain
和draw
。例如cluster
、axis
、component
、forest
、time
...如果您想不出表示观测数量的维度(如时间)的有意义的名称,请回退到
obs_id
。对于矩阵维度,由于 xarray 不允许重复的维度名称,请添加
_bis
后缀。即param, param_bis
。对于堆叠
chain
和draw
产生的维度,请使用sample
,即.stack(sample=("chain", "draw"))
。我们经常需要将分类变量编码为整数。在编码的变量名称中添加
_idx
。即从floor
和county
到floor_idx
和county_idx
。为避免在使用
pm.Data
时发生冲突和覆盖变量,请使用以下模式x = np.array(...) with pm.Model(): x_ = pm.Data("x", x) ...
这避免了覆盖原始的
x
,同时拥有idata.constant_data["x"]
,并且在模型中,x_
仍然可用作x
的角色。否则,始终尝试使用与赋予 PyMC 随机变量的字符串名称相同的变量名。
绘图
Matplotlib 图形和轴。使用
fig
表示 Matplotlib 图形ax
表示单个 Matplotlib 轴对象axs
表示 Matplotlib 轴对象数组
当手动处理多个 Matplotlib 轴时,使用局部
ax
变量fig, axs = pyplot.subplots() ax = axs[0, 1] ax.plot(...) ax.set(...) ax = axs[1, 2] ax.scatter(...)
fig, axs = pyplot.subplots() axs[0, 1].plot(...) axs[0, 1].set(...) axs[1. 2].scatter(...)
如果在重构子图时需要编辑代码,这会更容易,每个子图只需要更改一次,而不是每个 Matplotlib 函数调用更改一次。
通常,将 NumPy linspace 转换为
DataArray
以便 xarray 自动处理对齐和广播,并简化计算非常有用。如果需要维度名称,请使用
x_plot
如果原始数组和 DataArray 需要共存的变量名,请添加
_da
后缀
因此,最终代码如下所示
x = xr.DataArray(np.linspace(0, 10, 100), dims=["x_plot"]) # or x = np.linspace(0, 10, 100) x_da = xr.DataArray(x)
循环
使用 enumerate 时,取变量的第一个字母作为计数
for p, person in enumerate(persons)
循环时,如果需要在使用循环索引进行子集化后存储变量,请将用于循环的索引变量附加到原始变量名
variable = np.array(...) x = np.array(...) for i in range(N): variable_i = variable[i] for j in range(K): x_j = x[j] ...
第一个单元格#
所有示例 Notebook 的第一个单元格都应具有 MyST 目标、级别 1 Markdown 标题(即带有单个 #
的标题)以及 post 指令。语法如下
(notebook_name)=
# Notebook Title
:::{post} Aug 31, 2021
:tags: tag1, tag2, tags can have spaces, tag4
:category: level
:author: Alice Abat, Bob Barceló
:::
日期应与最新的更新/执行日期相对应,至少大致如此(如果由于 PR 合并前的审查过程导致日期偏差几天,则没有问题)。这将允许用户查看哪些 Notebook 最近已更新,并将帮助 PyMC 团队确保没有 Notebook 过时太久。
重要提示
MyST 目标((notebook_name)=
位)用于在 Notebook 之间链接。它必须是 Notebook 特定的,例如其文件名。请勿复制粘贴此内容并保持 notebook_name
不修改
标签可以是任何内容,但我们要求您尝试使用现有标签,以避免标签列表变得太长。
每个 Notebook 都应具有一个或两个类别,指示
Notebook 的级别(必需)
beginner
(站立的乌鸦图标)intermediate
(飞翔的鸽子图标)advanced
(龙图标)
diataxis 类型(对于旧 Notebook 是可选的)
教程
操作指南
解释
参考
作者应列出编写、改编或更新 Notebook 的人员,不包括那些仅重新执行 Notebook 且代码或措辞几乎没有更改的人员。此处应仅添加作者姓名,因为这只是 Notebook 的元数据,自我推销链接和有关更改的详细信息应添加到“作者”部分,请参阅 作者身份和署名 了解更多详细信息。
额外依赖项#
如果 Notebook 使用了非 PyMC 依赖项的库,则应指示这些额外依赖项,并提供有关如何安装它们的一些建议。这确保读者事先知道他们需要安装什么,并且例如可以决定是在本地运行还是在 Binder 上运行。
为了方便 Notebook 编写者和维护者,pymc-examples 包含一个模板,用于警告额外的依赖项,并在下拉菜单中提供特定的安装说明。
因此,具有额外依赖项的 Notebook 应
使用
myst_substitutions
类别将额外的依赖项列为 Notebook 元数据,然后列出extra_dependencies
或pip_dependencies
和conda_dependencies
。此外,还有一个extra_install_notes
用于在下拉菜单中包含自定义文本。可以从菜单中编辑 Notebook 元数据,方法是选择
这将打开一个窗口,其中包含 JSON 格式的文本,可能看起来有点像
{ "kernelspec": { "name": "python3", "display_name": "Python 3 (ipykernel)", "language": "python" }, "language_info": { "name": "python", "version": "3.9.7", "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py" } }
{ "kernelspec": { "name": "python3", "display_name": "Python 3 (ipykernel)", "language": "python" }, "language_info": { "name": "python", "version": "3.9.7", "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py" }, "myst": { "substitutions": { "extra_dependencies": "bambi seaborn" } } }
{ "kernelspec": { "name": "python3", "display_name": "Python 3 (ipykernel)", "language": "python" }, "language_info": { "name": "python", "version": "3.9.7", "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py" }, "myst": { "substitutions": { "pip_dependencies": "graphviz", "conda_dependencies": "python-graphviz", } } }
pip 和 conda 特定键会覆盖
extra_installs
键,因此如果使用它们,则使用extra_installs
没有意义。要么定义了 pip 和 conda 替换,要么都没有定义。
在导入额外的依赖项之前,包含带有以下 Markdown 的警告和安装建议模板
:::{include} ../extra_installs.md :::
代码前导#
在导入 Matplotlib 和/或 ArviZ 的单元格(通常是第一个单元格)正下方的单元格中,将 ArviZ 样式设置为 darkgrid(这必须在与 Matplotlib 导入不同的单元格中,因为 Matplotlib 设置其默认值的方式)
RANDOM_SEED = 8927
rng = np.random.default_rng(RANDOM_SEED)
az.style.use("arviz-darkgrid")
在生成合成数据时,一个好的做法是像上面那样设置随机种子,以提高可重复性。此外,请检查收敛性(例如 assert all(r_hat < 1.03)
),因为我们有时会自动重新运行 Notebook,而没有仔细检查每个 Notebook。
从文件读取#
使用 try... except
子句加载数据,并在 except 路径中使用 pm.get_data
。这将确保克隆了 pymc-examples 仓库的用户将读取其数据的本地副本,同时对于没有本地副本的用户,也从 GitHub 下载数据。这是一个示例
try:
df_all = pd.read_csv(os.path.join("..", "data", "file.csv"), ...)
except FileNotFoundError:
df_all = pd.read_csv(pm.get_data("file.csv"), ...)
pre-commit 和代码格式化#
我们在持续集成期间对 Notebook 运行一些代码质量检查。确保您的 Notebook 通过 CI 检查的最简单方法是使用 pre-commit。您可以使用以下命令安装它
pip install -U pre-commit
然后使用以下命令启用它
pre-commit install
然后,代码质量检查将在您每次提交任何更改时自动运行。要手动运行代码质量检查,您可以执行以下操作,例如
pre-commit run --files notebook1.ipynb notebook2.ipynb
将 notebook1.ipynb
和 notebook2.ipynb
替换为您修改的任何 Notebook。
注意:有时,Black 会令人沮丧(好吧,谁不是呢?)。在这些情况下,您可以为特定代码行禁用其魔力:只需编写 #fmt: on/off
即可禁用/重新启用它,如下所示
# fmt: off
np.array(
[
[1, 0, 0, 0],
[0, -1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, -1],
]
)
# fmt: on
参考文献#
参考文献应以 BibTeX 格式添加到 references.bib
文件中,并在 Notebook 文本中相关位置使用 sphinxcontrib-bibtex 引用。
.bib
文件中的参考文献的 ID 应类似于 authorlastnameYEARkeyword
或 libraryYEARkeyword
(对于文档页面),并且应按此 ID 按字母顺序排序,以便于在文件中查找参考文献并防止添加重复的参考文献。
参考文献可以在单个 Notebook 中引用两次。两种常见的参考文献格式是
{cite:p}`bibtex_id` # shows the reference author and year between parenthesis
{cite:t}`bibtex_id` # textual cite, shows author and year without parenthesis
可以在文本本身内内联添加。在 Notebook 的末尾,使用以下 Markdown 添加书目
## References
:::{bibliography}
:filter: docname in docnames
:::
或者,如果您想添加文本中未引用的额外参考文献,请使用
## References
:::{bibliography}
:filter: docname in docnames
extra_bibtex_id_1
extra_bibtex_id_2
:::
水印#
watermark
是一个库,它可以自动打印 Python 和您用于运行 NB 的软件包的版本 – 再现性至上!
如果您安装了我们的 requirements-dev.txt
,则此库应在您的虚拟环境中。否则,运行 pip install watermark
。
首先,添加一个 Markdown 单元格,标题仅为 ## Watermark
,使其显示在目录中。这是倒数第二个部分,位于尾声/页脚上方。然后,添加一个代码单元格以打印 Notebook 中使用的 Python 和软件包的版本。这是 Notebook 中的最后一个代码单元格。
p
标志是可选的(或者可能需要将不同的库作为输入),但如果未显式导入 PyTensor 或 xarray,则应添加该标志。这也将由 pre-commit
检查(因为我们有时都会忘记做事情 😳)。
## Watermark
%load_ext watermark
%watermark -n -u -v -iv -w -p pytensor,xarray
尾声#
Notebook 中的最后一个单元格应是 Markdown 单元格,内容完全如下
:::{include} ../page_footer.md
:::
唯一的例外是不在通常位置的 Notebook,因此需要更新页面页脚的路径才能使 include 工作。
您现在已准备就绪 🎉。您可以推送您的更改,打开 pull request,并在合并后,以圆满完成工作的自豪感休息 👏。非常感谢您对开源的贡献,我们真的非常感谢!