基本操作¶
这里我们将讨论在 SymPy 中进行表达式操作所需的一些最基本的操作。一些更高级的操作将在后面的 高级表达式操作 部分讨论。
>>> from sympy import *
>>> x, y, z = symbols("x y z")
替换¶
你可能想要对数学表达式做的最常见的事情之一是替换。替换用其他东西替换表达式中所有实例。它使用 subs
方法完成。例如
>>> expr = cos(x) + 1
>>> expr.subs(x, y)
cos(y) + 1
替换通常出于以下两个原因之一进行
在某点评估表达式。例如,如果我们的表达式是
cos(x) + 1
,并且我们想要在点x = 0
处评估它,以便我们得到cos(0) + 1
,也就是 2。>>> expr.subs(x, 0) 2
用另一个子表达式替换子表达式。我们可能出于两个原因想要这样做。第一个是我们试图构建具有某种对称性的表达式,例如 \(x^{x^{x^x}}\)。为了构建它,我们可能从
x**y
开始,并将y
替换为x**y
。然后我们将得到x**(x**y)
。如果我们在这个新表达式中将y
替换为x**x
,我们将得到x**(x**(x**x))
,即我们想要的表达式。>>> expr = x**y >>> expr x**y >>> expr = expr.subs(y, x**y) >>> expr x**(x**y) >>> expr = expr.subs(y, x**x) >>> expr x**(x**(x**x))
第二种情况是,我们想要执行一个非常受控的简化,或者可能 SymPy 无法执行的简化。例如,假设我们有 \(\sin(2x) + \cos(2x)\),我们想用 \(2\sin(x)\cos(x)\) 替换 \(\sin(2x)\)。正如我们稍后将要学习的,函数
expand_trig
可以做到这一点。但是,此函数也会扩展 \(\cos(2x)\),而我们可能不希望这样做。虽然有一些方法可以执行这种精确的简化,我们将在 高级表达式操作 部分学习其中的一些方法,但一种简单的方法是直接用 \(2\sin(x)\cos(x)\) 替换 \(\sin(2x)\)。>>> expr = sin(2*x) + cos(2*x) >>> expand_trig(expr) 2*sin(x)*cos(x) + 2*cos(x)**2 - 1 >>> expr.subs(sin(2*x), 2*sin(x)*cos(x)) 2*sin(x)*cos(x) + cos(2*x)
关于 subs
,需要注意两点。首先,它返回一个新的表达式。SymPy 对象是不可变的。这意味着 subs
不会就地修改它。例如
>>> expr = cos(x)
>>> expr.subs(x, 0)
1
>>> expr
cos(x)
>>> x
x
在这里,我们看到执行 expr.subs(x, 0)
不会更改 expr
。事实上,由于 SymPy 表达式是不可变的,因此任何函数都不会就地更改它们。所有函数都会返回新的表达式。
要一次执行多个替换,请将 (old, new)
对的列表传递给 subs
。
>>> expr = x**3 + 4*x*y - z
>>> expr.subs([(x, 2), (y, 4), (z, 0)])
40
将此与列表推导结合起来,可以一次性进行大量类似的替换。例如,假设我们有 \(x^4 - 4x^3 + 4x^2 - 2x + 3\),我们想用 \(y\) 替换所有偶数次方的 \(x\),得到 \(y^4 - 4x^3 + 4y^2 - 2x + 3\)。
>>> expr = x**4 - 4*x**3 + 4*x**2 - 2*x + 3
>>> replacements = [(x**i, y**i) for i in range(5) if i % 2 == 0]
>>> expr.subs(replacements)
-4*x**3 - 2*x + y**4 + 4*y**2 + 3
将字符串转换为 SymPy 表达式¶
函数 sympify
(即 sympify
,不要与 simplify
混淆)可用于将字符串转换为 SymPy 表达式。
例如
>>> str_expr = "x**2 + 3*x - 1/2"
>>> expr = sympify(str_expr)
>>> expr
x**2 + 3*x - 1/2
>>> expr.subs(x, 2)
19/2
警告
sympify
使用 eval
。不要将其用于未经清理的输入。
evalf
¶
要将数值表达式评估为浮点数,请使用 evalf
。
>>> expr = sqrt(8)
>>> expr.evalf()
2.82842712474619
SymPy 可以将浮点表达式评估到任意精度。默认情况下,使用 15 位精度,但您可以将任何数字作为参数传递给 evalf
。让我们计算 \(\pi\) 的前 100 位数字。
>>> pi.evalf(100)
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
要以数值方式评估具有符号的表达式,我们可以使用 subs
后跟 evalf
,但更有效且数值上更稳定的是使用 subs
标志将替换传递给 evalf
,该标志接受 Symbol: point
对的字典。
>>> expr = cos(2*x)
>>> expr.evalf(subs={x: 2.4})
0.0874989834394464
有时,表达式评估后,仍然会存在小于所需精度的舍入误差。用户可以自行决定是否通过将 chop
标志设置为 True 来删除此类数字。
>>> one = cos(1)**2 + sin(1)**2
>>> (one - 1).evalf()
-0.e-124
>>> (one - 1).evalf(chop=True)
0
lambdify
¶
如果您想进行简单的评估,subs
和 evalf
很不错,但如果您打算在多个点评估表达式,则有更有效的方法。例如,如果您想在一千个点评估表达式,使用 SymPy 会比需要的速度慢得多,尤其是在您只关心机器精度的情况下。相反,您应该使用 NumPy 和 SciPy 等库。
将 SymPy 表达式转换为可进行数值评估的表达式最简单的方法是使用 lambdify
函数。 lambdify
的作用类似于 lambda
函数,只是它将 SymPy 名称转换为给定数值库(通常是 NumPy)的名称。例如
>>> import numpy
>>> a = numpy.arange(10)
>>> expr = sin(x)
>>> f = lambdify(x, expr, "numpy")
>>> f(a)
[ 0. 0.84147098 0.90929743 0.14112001 -0.7568025 -0.95892427
-0.2794155 0.6569866 0.98935825 0.41211849]
警告
lambdify
使用 eval
。不要将其用于未经清理的输入。
您可以使用 NumPy 以外的其他库。例如,要使用标准库数学模块,请使用 "math"
。
>>> f = lambdify(x, expr, "math")
>>> f(0.1)
0.0998334166468
要将 lambdify 与其不知道的数值库一起使用,请传递 sympy_name:numerical_function
对的字典。例如
>>> def mysin(x):
... """
... My sine. Note that this is only accurate for small x.
... """
... return x
>>> f = lambdify(x, expr, {"sin":mysin})
>>> f(0.1)
0.1