封面设计:Joshua Hoiberg
handcalcs:
在Jupyter中进行Python计算,
就像手写一样。
handcalcs
是一个库,可以自动将Python计算代码渲染成Latex,但其方式模仿了用铅笔手写计算的格式:先写出符号公式,然后是数值代入,最后是结果。
由于handcalcs
显示了数值代入过程,计算变得更容易通过手工检查和验证。
目录
基本演示
安装
你可以使用pip安装:
pip install handcalcs
要安装可选的nbconvert"无输入"导出器,请使用:
pip install "handcalcs[exporters]"
新功能
从v1.9.0版本开始,handcalcs不再安装"无输入"nbconvert导出器。这是为了减轻handcalcs的安装负担并确保包的适当范围。nbconvert导出器现在"超出范围",并在https://github.com/connorferster/nb-hideinputs单独维护。
基本用法1:作为Jupyter单元格魔法命令(%%render
)
handcalcs
旨在与Jupyter Notebook或Jupyter Lab一起使用,作为一个_单元格魔法命令_。
首先,导入模块并运行单元格:
import handcalcs.render
注意:这将在Jupyter Notebook中导入
%%tex
和%%render
魔法命令。
然后,在任何你想用handcalcs
渲染的单元格中,只需在单元格顶部使用render单元格魔法命令:
%%render
例如:
%%render
a = 2
b = 3
c = 2*a + b/3
就是这样!
渲染后,如果你的系统上安装了Latex环境,你就可以将笔记本导出为PDF。如果你是Latex新手,想在系统上安装它以使用此功能,请参阅wiki中的安装Tex部分。
你还可以使用%%tex
命令将任何Python代码片段转换为有效的LaTex。例如:
首先导入handcalcs
。我们还从__math__包中导入了一些属性/函数,用于下面的示例。
import handcalcs.render
from math import sqrt, pi
现在,你也可以使用%%tex
魔法命令!
%%tex
a = 2 / 3 * sqrt(pi)
这将产生如下LaTeX输出:
\[
\begin{aligned}
a &= \frac{ 2 }{ 3 } \cdot \sqrt{ \pi } = \frac{ 2 }{ 3 } \cdot \sqrt{ 3.142 } &= 1.182
\end{aligned}
\]
基本用法2:作为函数的装饰器,@handcalc()
感谢@eriknw开发innerscope并主动将其集成到handcalcs
中。谢谢!
首先导入@handcalc()
装饰器:
from handcalcs.decorator import handcalc
@handcalc([override: str = "", precision: int = 3, left: str = "", right: str = "", jupyter_display: bool = False])
返回一个由 (latex_code: str, locals: dict)
组成的元组,其中 locals
是函数命名空间内所有变量的字典。
override
是表示可接受的覆盖标签之一的字符串(见下文)precision
是一个整数,用于改变显示的小数精度left
和right
是可以在编码后的 Latex 字符串前后添加的字符串,如\\[
和\\]
或$
和$
- 当
jupyter_display
为 True 时,将只返回locals
字典,而会使用IPython.display
中的display(Latex(latex_code))
显示编码后的 Latex 字符串渲染。如果不在 Jupyter 环境中使用,将会返回错误 - 当
record
为 True 时,将激活HandcalcsCallRecorder
以允许函数"回溯"先前的输出(见下文)v1.8.0 新功能
在你装饰的函数中,从 def my_calc(...)
到 return 语句(如果有)之间的所有内容现在就像 Jupyter 单元格中的代码,只不过它是一个标准的 Python 函数。
以这种方式使用时,你可以使用 @handcalc()
来动态生成 Latex 代码,以在 Jupyter 和非 Jupyter Python 环境(如 streamlit)中显示。
HandcalcsCallRecorder(v1.8.0 新功能)
HandcalcsCallRecorder
是一种新的函数包装器,可以通过 @handcalc
装饰器使用。要激活它,请在装饰器函数的参数中选择 record=True
。
其预期用例是在迭代过程中。在工程中,通常需要在表格或 DataFrame 中计算大量值。表格本身包含计算结果,但不一定显示计算步骤。HandcalcsCallRecorder
允许你显示已由装饰函数处理的计算迭代之一的计算过程,如下例所示:
全局配置选项(v1.6.0 新功能)
这是 handcalcs 的一个重要新版本,引入了全局配置功能。这允许用户控制 handcalcs 工作方式的多个选项。配置选项及其默认值如下:
decimal_separator = "."
latex_block_start = "\\["
latex_block_end = "\\]"
math_environment_start = "aligned"
math_environment_end = "aligned"
line_break = "\\\\[10pt]"
use_scientific_notation = False
display_precision = 3
underscore_subscripts = True
greek_exclusions = []
param_columns = 3
preferred_string_formatter = "L"
custom_symbols = {}
配置 API
import handcalcs.render
handcalcs.set_option("display_precision", 4)
handcalcs.set_option("param_columns", 5)
handcalcs.set_option("line_break", "\\\\[20pt]")
handcalcs.set_option("greek_exclusions", ["psi"]) # 等等...
这些更改现在会影响当前会话中渲染的所有单元格。如果你想永久更新 config.json
文件以保存这些更改(这样 handcalcs 在下次启动时就会使用这些选项),你可以调用 handcalcs.save_config()
,这些更改将被保存(并在下一个会话中立即可用)。
自定义符号(v1.7.0 新功能)
你现在可以向全局配置添加自定义符号,以处理 handcalcs 未考虑到的所有情况。
例如:
handcalcs.set_option("custom_symbols", {"V_dot": "\\dot{V}", "N_star": "N^{*}"})
现在将允许这种渲染:
handcalcs.set_option()
函数的文档字符串展示了可用的选项及其取值。
覆盖标签
handcalcs
对你希望如何格式化计算做出了某些假设,在这方面不允许进行大量定制。然而,目前有四种定制可以使用 # override tags
作为 %%render
单元格魔术后的参数来实现。此外,你还可以指定要显示的小数精度。每个单元格只能使用一个覆盖标签,但你可以将覆盖标签与精度设置结合使用。
覆盖标签可以与 Jupyter 单元格魔术和函数装饰器一起使用。要在装饰器中使用覆盖标签,只需将其作为参数提供,例如 @handcalc(override='params', precision=2)
我将比较二次方程公式的基本渲染(如下)与每个覆盖标签实现的变化。
params
:
handcalcs
垂直渲染代码行,一行接一行。然而,当你分配变量或显示结果变量时,你可能不想浪费所有的垂直空间。
使用 params
覆盖标签,你的参数列表将改为以三列渲染,从而节省垂直空间。此外,只会显示结果,不会显示计算过程。
调整精度:
单元格中的小数位数可以通过在%%render
后提供一个整数来调整,以指示要显示的小数精度。可以与其他覆盖标签组合使用。
long
和short
:
为了节省垂直空间,handcalcs
会尝试判断计算的长度,如果足够短,就会将其完整地呈现在一行上。
如果handcalcs
的内部测试认为计算太长而无法放在一行上,它会将其分成多行。
使用# long
或# short
覆盖标签可以覆盖长度检查,并以"长"格式或"短"格式显示单元格中的所有计算。例如:
long:跨多行展示,就像你有一个长方程一样
short:强制显示在单行上,就像你有一个短方程一样
# "短"计算的格式(可以放在一行中):
c = 2*a + b/3 = 2*(2) + (3)/3 = 5
# "长"计算的格式(需要多行格式)
c = 2*a + b/3
= 2*(2) + (3)/3
= 5
symbolic
handcalcs
的主要目的是渲染完整的计算过程,包括数值替换。这允许轻松地追踪和验证计算。
然而,在某些情况下,可能更希望以符号形式显示计算。例如,你可以使用symbolic
标签将handcalcs
作为一种快速渲染符号形式Latex方程的方法。
或者,你可能更喜欢在一个单元格中渲染所有输入参数,在下一个单元格中以符号形式渲染公式,然后在最后一个单元格中渲染所有最终值,完全跳过数值替换过程。
请记住,即使你在计算中使用symbolic
标签,你仍然需要提前声明这些变量(通过给它们赋值)以使你的计算成为有效的Python代码。
sympy
这个标签仅适用于已加载sympy
的情况。Sympy允许对代数表达式进行符号操作、求解和积分。Sympy会自行将其对象渲染为Latex,无需handcalcs。
如果你正在操作sympy表达式或方程以进行计算,可以使用handcalcs
来处理结果表达式的替换和计算。
注意:将符号变量重新赋值为数字会覆盖它们作为sympy变量的性质。不过,你现在已经完成了这些操作,对吧?所以这不是问题。如果你需要再次进行符号运算,只需从头重新运行你的notebook单元格即可。
单位包兼容性
handcalcs
设计时考虑了与单位包forallpeople的兼容性(而forallpeople也设计为与handcalcs
兼容)。然而,最近有报告称pint也可以很好地工作。
关于与其他单位包的潜在兼容性,请参阅wiki
特性
快速显示多个变量的值
不再需要print
语句。只需将你的变量放在一行上,它们就会全部显示出来。
仅获取Latex代码,不渲染
如果你只想直接生成渲染后的Latex代码以在自己的Latex文件中使用,可以使用%%tex
单元格魔法:
%%tex
a = 2
b = 3
c = 2*a + b/3
然后你可以直接复制结果并粘贴到你自己的LaTeX文档中。
下标(以及子下标等)
当变量名中使用_
时,会自动创建下标。连续使用多个_
会创建嵌套的子下标。
希腊符号
任何包含希腊字母(如"pi"、"upsilon"、"eta"等)作为字符串或子字符串的变量名将被替换为表示该希腊字母的适当Latex代码。
符号 | 替换 | 符号 | 替换 |
---|---|---|---|
alpha | α | Alpha | Α |
beta | β | Beta | Β |
gamma | γ | Gamma | Γ |
delta | δ | Delta | Δ |
epsilon , varepsilon | ϵ, ε | Epsilon | Ε |
zeta | ζ | Zeta | Ζ |
eta | η | Eta | Η |
theta , vartheta | θ, ϑ | Theta | Θ |
iota | ι | Iota | Ι |
kappa | κ | Kappa | Κ |
lamb | λ | Lamb | Λ |
mu | μ | Mu | Μ |
nu | ν | Nu | N |
xi | ξ | Xi | Ξ |
omicron | ο | Omicron | Ο |
pi , varpi | π, ϖ | Pi | Π |
rho , varrho | ρ, ϱ | Rho | Ρ |
sigma , varsigma | σ, ς | Sigma | Σ |
tau | τ | Tau | Τ |
upsilon | υ | Upsilon | Υ |
phi , varphi | φ, ϕ | Phi | Φ |
chi | χ | Chi | Χ |
psi | ψ | Psi | Ψ |
omega | ω | Omega | Ω |
-
使用小写字母作为变量名将生成小写希腊字母。
-
使用大写字母开头的变量名将生成大写希腊字母。
函数,内置或自定义
如果你在计算中使用Python函数,例如min()
或tan()
,它们将被替换为表示该函数的Latex代码。
如果你创建自己的函数,它们将在Latex中被渲染为自定义运算符。
如果你使用名为sqrt
的函数(无论是你自己的自定义实现还是来自math.sqrt
),它将被渲染为根号符号。
渲染行内注释
放在计算行后的任何注释都将在Latex中被渲染为行内注释。
这使得在计算旁边添加注释变得方便,可以简要解释你可能获得或推导出某个特定值的来源。
跳过替换
完全包裹在括号()
中的任何计算将仅渲染为param = result
,而不进行替换。
当你想即时计算一个参数但不希望它成为计算的焦点时,这会很方便。
条件语句
现实世界中的许多计算都依赖于具体情况。
handcalcs
允许以一种更容易理解计算上下文的方式在其代码中包含一些简单的条件语句。
注意:条件表达式后可以使用多个"行"的计算,只要它们都在同一行上并用";
"分隔。更多背景信息请参见预期行为。
数值积分
你可以使用scipy.quad
对预定义函数进行数值积分,并让handcalcs
对其进行基本渲染。
如果你使用名称中包含integrate
或quad
的函数,就会触发这种行为。
"Prime"记号
有时你需要在变量上写"prime":
Jupyter中的PDF打印
注意:自 nbconvert v6.0 起,不再需要安装模板(如旧版 YouTube 视频所示)。安装 handcalcs 时会同时安装适用于 Jupyter Notebook/Lab 的 Exporter,为您提供两个新的"文件 -> 保存并导出为"选项:
- 导出 HTML_NoInput
- 导出 LaTeX_NoInput
- 导出 PDF_NoInput
这些选项会隐藏所有输入单元格,使您在 Jupyter 笔记本中只能看到渲染后的输出。
通过使用这三个选项,您可以通过 HTML(然后从浏览器打印 PDF)或通过 LaTeX(直接或通过您自己的 LaTeX 环境)创建 PDF 导出。
预期行为
handcalcs 旨在渲染用 Python 代码编写的算术计算。它并非用于将任意 Python 代码渲染为 LaTeX。
鉴于此,handcalcs 仅渲染 Python 的一小部分子集,许多内容无法正常工作,尤其是跨多行的内容(如函数定义、for 循环、with 语句等)。
handcalcs 通过解析单元格内的单独 Python 行来工作。它不会将单元格作为一个整体进行解析。因此,所有要渲染的语句必须包含在单行内。
接受的数据类型
handcalcs 会尝试渲染所有数据类型。然而,它目前还不能渲染所有基于"集合"的数据类型,如 list 和 dict。如果您使用集合来保存参数函数,例如 sum((23, 123, 45)),请使用元组以确保正确渲染。或者,您可以在 handcalcs 中使用一维 numpy 数组(向量)。
对象通过两种主要方法渲染为 LaTeX:
-
如果对象定义了 repr_latex() 方法,则使用该方法。
a) 如果对象有其他将自身渲染为 LaTeX 代码的方法,如 .latex() 或 .to_latex(),也会尝试使用。
为了正确渲染表示,对象的 LaTeX 表示必须使用 MathJax 和/或 KaTeX 实现的命令。
-
如果对象没有 LaTeX 方法,则使用 str()。
如果您使用的对象类型具有渲染为 <MyObject: value=34> 的 str 方法,那么 LaTeX 解释器将看到并尝试渲染这个内容。
算术运算符
-
- 渲染为 +
-
- 渲染为 -
-
- 渲染为"点运算符"(LaTeX:\cdot)
- / 始终渲染为分数
- ** 渲染为上标
- % 渲染为"模函数"(LaTeX:\mod)
目前不支持渲染 //,但您可以使用 math.floor 函数作为替代(作为 floor)。
for 循环和其他迭代
目前不支持显示渲染后的迭代。预期用法是在不渲染的单元格中执行迭代,然后在单独的单元格中渲染迭代产生的最终结果值。
注意事项
由于 handcalcs 设计用于 Jupyter 环境,且 Jupyter 单元格可以乱序运行,如果您在整个笔记本中重复使用变量名,可能会导致一堆渲染精美但完全错误的计算结果。
handcalcs 使用笔记本的用户命名空间字典来查找命名空间中所有变量的值。如果您的计算在整个笔记本中重复使用变量名,那么当您以非预期顺序运行单元格时,该名称的字典条目可能与您想象的不同。
如果单元格按正确顺序运行(从上到下更容易),您可以在整个笔记本中有效地重复使用变量名。
关于这一点:如果您使用 handcalcs 进行可能成为法律文件的报告(如设计工程计算),确保结果符合您的预期是您的责任。handcalcs 是免费开源软件,作者不对使用它而导致的错误计算负责。
话虽如此,handcalcs 渲染数学的方式的目的就是让手动确认和验证计算变得非常容易。
YouTube 教程
handcalcs 入门(假设零 Python 知识)
https://www.youtube.com/watch?v=ZNFhLCWqA_g
工程计算:handcalcs-on-Jupyter vs. Excel
https://www.youtube.com/watch?v=n9Uzy3Eb-XI
应用和与其他人的包的兼容性
**请查看wiki,了解 handcalcs 在教育和工程中的应用,以及与其他 Python 库(如 streamlit 和 papermill)一起使用 handcalcs 的示例。