# 9.1 SymPy初步

### 9.1.1 初识SymPy

SymPy是一个数学符号计算库(sym代表了symbol,符号)，包括了积分、微分方程等各种数学运算方法，为Python提供了强大的数学运算支持。它保证自身的代码尽可能简单，且易于理解，容易扩展。它完全由Python写成，不依赖于外部库。

我们之所以选择SymPy，主要出于以下原因：

免费：SymPy遵循BSD开源协议。使用者可以自由地使用，修改源代码，甚至作商业用途。

基于Python: SymPy完全是用Python编写的，并使用Python作为其语言，拥有Python语言简洁、易于理解等优良特性。

轻量: SymPy只依赖于个用于任意浮点运算的纯Python库mpmath。

灵活:除了用作交互工具之外，SymPy还可以嵌入到其他应用程序中，并使用自定义函数进行扩展。

### 9.1.2 安装SymPy

我们可以使用conda命令或者pip命令对SymPy进行安装：

conda install sympy

pip instal sympy

安装完成，即可使用普通模块导入方法，对sympy库进行导入。为了方便起见，我们在本章开始对SymPy包下所有模块进行导入，但是为了了解各个模块的归属，我们会在新使用模块第一次出现的地方，使用from import语句对其进行二次加载，虽然这样做并不会真正地二次加载。

\[1]:from sympy import \*

另外，让我们在Cell \[1]中继续键入下面几行代码，并运行。

init\_session()

这将会给我们之后讲述带来便利。我们会在接下来的课程中对上述代码进行讲解。

### 9.1.3 SymPy语法

#### 数学符号的创建

一门优秀编程语言最重要的优点之一即：贴近自然语言。SymPy尽可能地保留了原有的数学习惯，但是不可避免地在某些情况下，还需要进行额外的声明，来保证代码语义的确定性。

我们使用sympy库下的symbols函数来创建一个数学变量。

symbols函数的使用方法如下：

symbols(‘str1\[, str2, …]’)

symbols的传入参数为一个或多个字符串对象，依次返回对应的sympy符号对象类型。

x

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-ee960222a6761b7ba23dce37aa79dc46274953ed%2F4f06a5462d4697b5dae2832b8f063140.png?alt=media)[2](https://github.com/JohannesLiu/Data_Science_Python_Implemented/blob/main/Chapter9/x=symbols\(%E2%80%98x%E2%80%99\)/README.md):

\[3:y, z, k=symbols(‘z k y’)

y, z, k

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-6879b75a318d72ed3e9f8725e1c202792b9898e2%2F7f89d3ee3c75789d91eebf7806bca8a2.png?alt=media)\[3]

在这种赋值情况下，变量z，k，y对应的符号不再是’z’, ‘k’, ‘y’，而是发生了错位。这也就是说，变量的实际符号，与变量的名称无关。

#### 符号表达式

在数学上，等号‘=’通常有两层含义，赋值与等价。当我们尝试为变量S赋值时如，我们不会认为等号的意义是等价，当我们在解含有未知量x的方程的时候，如，我们同样也不会认为等号的意义是赋值。在Python中，我们使用=代表赋值，==号代表等价，而SymPy保留了这个传统。

但是，Python中的等价与数学上的等价又有区别，Python中的等价会严格比较’==’左右的表达式结构，而数学上的等价则是比较两端化简规约后是否相等。

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-0613a8e13e3bbed1a4f5222fcd15cf002ee629bc%2F381b3d7948021ce973fcb4dd737da488.png?alt=media)\[4]:(x+1)\*\*2==x\*\*2+x\*2+1

\[4]:

如果您学习过Matlab或Mathematica等编程语言，那么通常我们会习惯性地用^来进行幂运算，用/来进行除法运算。在Python中双星号\*\*代替了^的职能，^的功能是进行异或运算，而/的作用则是进行数值除法运算。

如果我们希望保留分数形式，那么我们需要使用SymPy下的Rational函数

Rational函数的用法如下：

Rational(dividend, divisor)

例

0.3333333333333333

\[6]:from sympy import Rational

Rational(1, 3)

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-701334dbe0acd95a84f9bbb09bd62ee947bb7238%2Fb310c6f1222647f3e55ca0eb66d4cf88.png?alt=media)\[6]:

#### 当 SymPy遇见Python

最后我们将要探讨当SymPy数据类型与Python数据类型进行混合计算时，计算结果的数据类型问题。

当多个SymPy数据类型进行计算时，会按定义在SymPy数据类型上的操作方法进行，计算结果仍然为SymPy类型。当SymPy与Python数据类型进行混合运算需要遵循由简到繁的原则，即先Python内置数据类型要转换为SymPy数据类型，然后按照SymPy数据类型的运算规则进行计算

例

\[]:Rational(Integer (1)/Integer(2))

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-fe43a076c91d00ced65faae4a05c628bdc7b8f08%2Fcf0e66081e3acf1dbd190f77981b6fee.png?alt=media)\[]:

\[]:type(Rational(Integer (1)/Integer(2)))

\<class 'sympy.core.numbers.Half'>

\[]:Rational(Integer (1)/Integer(2))+1

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-f47596d30626d0920b8fe10a7e00fc34ff3424ab%2F2648451c1f63e9e0dd438c8d912976ea.png?alt=media)\[]:

\[]:type(Rational(Integer (1)/Integer(2))+1)

\<class 'sympy.core.numbers.Rational'>

SymPy集成了各种数学工具，在实践中，这些工具使用频繁。因此，我们通常将这些工具一次导入。

from sympy import \*

### 9.2.1 基本操作

在这里，我们讨论一些最基本的手术需要的表现手法和症状（？）。高级表达式操作部分稍后将讨论一些更高级的操作。

为了讲解方便，我们先创建三个符号变量。

\>>>x, y, z=symbols(“ x y z”)

#### 替换

使用数学表达式最常见的一种方法是替换。替换将表达式中某物的所有实例替换为另一物。这是使用symbols对象的subs方法完成的。

普通替换

\[]:expr=cos(x)+sin(pi/2)

expr.subs(x, pi/2)

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-8e0497dc28a058d8ad7b1ab8c49deb5037072513%2Fbb7efb11fd57d888409d73c9746db245.png?alt=media)\[]:

例：循环替换，执行5次除法嵌套

\[]:expr=1/(1+x)

for i in range(5): expr=expr.subs(x, 1/(1+x))

expr

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-29f3f1f7023cf2c51f1a35276d7b1bee2dd8f94a%2F2bdcaffd62c992db88bfb3d3cf9b32af.png?alt=media)\[]:

#### 字符串转为SymPy表达式

使用simpify函数，可以将字符串类型的表达式转为SymPy表达式

\[]:str\_expr=’x\*\*2+2\*x+1’

expr=sympify(str\_expr)

expr

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-e4cf183413b080e0d7147b7f0f83b91c4a3dde9f%2Fc56d5e2ec272a70a9a6e2ffb71659a50.png?alt=media)\[]:

#### 表达式求值

使用exalf(precision)可以对数值表达式求值，precision为小数保留位数，precision默认为15。

\[]:expr=sqrt(13)

expr.evalf(13)

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-711b646d5d81dc69d196e03c5813263ab3e0e24c%2F7104db456090018db6176cfcef8c2e55.png?alt=media)\[]:

#### 使用NumPy和SciPy计算

尽管subs和evalf可以很好的帮助我们进行计算，但是如果我们希望同时代入大量参数到一个公式进行计算，那么这时候你可能就忍受不了SymPy的运行效率了。

另外SymPy在计算精度上不如NumPy和SciPy，如果对机器精度有较高的要求的话，应该使用NumPy或SciPy这样的库。

将一个SymPy符号表达式转换成一个数值计算表达式最简单的方法是使用lambdify函数。lambdify的作用类似于lambda函数，只不过它将SymPy名称转换为特定数值库的名称，通常是NumPy。（？）

lambdify()函数用于转换表达式进行数值计算时需要指定三个参数，分别是变量，表达式和数值库。

例 将符号表达sin(x)+cos(x) 转为函数，并分别将0\~9十个整数代入计算。

\[]:import numpy

arr=numpy.arange(10)

expr=sin(x)+cos(x)

f=lambdify(x, expr, 'numpy')

type(f)

\[]:function

\[]:f(arr)

\[]:array(\[ 1. , 1.38177329, 0.49315059, -0.84887249, -1.41044612, -0.67526209,

0.68075479, 1.41088885, 0.84385821, -0.49901178])

### 9.2.2 打印

通过前面的例子，我们可以发现，Jupyter Lab可以对SymPy符号进行优美地展现。事实上，我们还可以通过自定义打印机，来根据我们实际需求打印不同风格的数学符号。常用的打印机有str，srepr，ASCII，Unicode，LaTex， MathML，Dot。

我们可以使用init\_print()函数自动启用环境中可用的最佳打印机以获得最好的打印效果。下面让我们尝试在默认Python解释器中输出下列积分符号：

\>>>from sympy import \*

\>>>x=symbols(‘x’)

\>>>Integral(sqrt(1/x), x)

\>>>init\_printing()

\>>>Integral(sqrt(1/x), x)

输出结果如图所示

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-d5839bd9674e5bd6f8f5df1cfdae7761cf1f1cc3%2F2a67e967a982c60ac90ba2501a1380e5.png?alt=media)

图

如果在交互式计算器类型的会话中工作，init\_session()函数将自动导入SymPy中的所有内容，与此同时，创建一些公共符号，设置绘图，并运行init\_printing()。如图所示

![](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-157e32e4f355ec9bdd1f2df3f581217958ff2840%2Fac100f2d41502bbad315babae1b9a2ec.png?alt=media)

图

此外，还需要注意以下几点：

1. 如果LATEX被安装，那么在Ipython QTConsole中，打印机将使用Latex
2. 要显式地不使用LATEX，请将use\_latex=False传递给init\_printing()或init\_session()。要显式地不使用Unicode，传递use\_unicode=False。
3. 在IPython控制台会话或常规Python会话中，如果终端支持Unicode，它将使用Unicode pretty打印机。在不支持Unicode的终端中，使用ASCII pretty打印机。

### 9.2.3 绘图

绘图模块允许您制作二维和三维绘图。目前，这些绘图是使用matplotlib作为后端呈现的。如果没有matplotlib，也可以使用textback绘制二维图。

表\[]列举了本节所用到的plot, plot\_parametric, plot3d, plot3d\_parametric函数的通用参数及其功能描述，在sympy.plotting模块中可以导入这些绘图函数，在实际绘图中，我们可以改变相应参数的取值来绘制不同的图形。如果您使用过Mathematica或者Matlab这些专业数学软件，会发现SymPy中存在大量与Mathematica和Matlab中同名的函数。这不仅仅表现在SymPy的绘图模块上，也表现在SymPy的积分，微分等模块。

表\[]

| 参数            | 描述                        |
| ------------- | ------------------------- |
| title         | 字符串，标题                    |
| xlabel        | 字符串，x轴坐标，                 |
| ylabel        | 字符串，y轴坐标                  |
| legend        | 布尔类型，显示图例                 |
| xscale        | {‘linear’,’log’} ，设置x轴范围， |
| yscale        | {‘linear’,’log’}，设置y轴范围   |
| axis          | 布尔类型，显示坐标轴                |
| axis\_center  | 浮点型二元组或{‘center’, ‘auto’} |
| xlim          | 浮点型，x轴的极限                 |
| yim           | 浮点型，y轴的极限                 |
| aspect\_ratio | 浮点型二元组或{‘auto’}           |
| autoscale:    | 布尔类型                      |
| margin        | 0\~1之间的浮点数，图边距            |

#### 二维图形

我们使用SymPy绘制的图形都是在特定的画布上展现的，画布上可以展示一张图也可以同时展示多张图。由于不同图形的范围不同，在同一画布上展示多种图形的时候不同图形的绘图范围也会不同。我们在对SymPy中的绘图函数进行讲解时，按照从单图到多图，从同一画布同范围到同一画布不同范围的方式进行展开。

**一元函数**

使用plot函数可以绘制一元函数代表的二维图形。

Plot()函数的用法如下：

单图

plot(expr, range, \*\*kwargs)

一张画布显示同范围多图

plot(expr1, expr2, ..., range, \*\*kwargs)

一张画布显示不同范围多图

plot((expr1, range), (expr2, range), ..., \*\*kwargs)

例：在统一张画布中绘制函数与，其中

\[]:from sympy.plotting import plot

\[]:p1=plot(x\*\*(-1), (x, -3, 3), ylim=(0, 10), show=False)

p2=plot(x, (x, -3, 3), show=False)

p1.append(p2\[0])

p1.show()

![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\11904061.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-4bdc2fbb8d06cdcfb2cedf7bf556466bee349558%2Fbbfdf5695d257e0405cd87ef4c41a887.png?alt=media)

在绘制图形的时候，变量p1存储的图形对象, p2存储的图形对象。如果将plot()函数中的show参数改为True，则会在创建plot对象的同时，显示plot图像。使用append方法可以将其他图形增加到已有图形对象的画布中去，使用show()方法可以显示图形对象。plot函数和其他绘图函数一样，默认返回一个列表，其中第0号元素存储第一个图形。因此在使用append方法追加p2列表对象存储的图形时，一定要指定图形所在的位置。在上例中同一样被分别存储在列表p1和p2的第0号位置。

例：在同张画布中绘制函数，其中

\[]:x=symbols('x')

p1=plot(x\*\*(-1),ylim=(0, 10), show=False)

p2=plot(x\*\*(1/2), show=False)

p3=plot(x, show=False)

p4=plot(x\*\*2, show=False)

p5=plot(x\*\*3, show=False)

p1.append(p2\[0])

p1.extend(p3)

p1.extend(p4)

p1.extend(p5)

p1.show()

使用extend方法可以将向extend方法中传入的列表对象中的图形元素全部追加到目标图形列表后。

![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\3BAEC217.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-64fb9bd746e7880f045d54bf9b3fad0d6a3e359f%2Fe08847a3fa4931a8c7f1691e5486ec2e.png?alt=media)

例：做出的函数趋势图。并将图形取名为graph of functions。

\[]:plot(x\*\*(-1), log(x), x, x\*log(x), x\*\*2, x\*\*3, (x, -3, 3), ylim=(0, 10), lengend=True, title='graph of functions')

![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\9F5F5D9.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-f4547a6f6859b09b995833c02582d3016201de0e%2F25cfd94e68fba239eee326afed762cbd.png?alt=media)

\[]:\<sympy.plotting.plot.Plot at 0x141d0400>

**参数方程**

SymPy中的plot\_parametric()函数可以绘制参数方程代表的二维图形。

plot\_parametric()函数的用法如下：

单图

plot\_parametric(expr\_x, expr\_y, range, \*\*kwargs)

一张画布显示同范围多图

plot\_parametric((expr1\_x, expr1\_y), (expr2\_x, expr2\_y), range, \*\*kwargs)

一张画布显示不同范围多图

plot\_parametric((expr\_x, expr\_y, range), ..., \*\*kwargs)

例：画出参数方程的图像

\[]:from sympy.plotting import plot\_parametric

\[]:plot\_parametric(cos(x), sin(x))

![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\2E5C2187.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-21815b81116d5ea74139779c4fe446115e45e684%2F7270b52b96b6713012bd95dc23b9a832.png?alt=media)

\[]:\<sympy.plotting.plot.Plot at 0x1430a588>

例：画出参数方程与

\[]:plot\_parametric((cos(x), sin(x)), (x, cos(x)))

![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\3052DC35.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-148026c262d5a9f70b8cc8821425a8304b5089d3%2Fc3d5616249b8982b5b5d69faa8971fc3.png?alt=media)

\[]:\<sympy.plotting.plot.Plot at 0xbb0f6d8>

#### 三维图形

**二元函数**

使用plot3d函数可以绘制二元函数代表的三维图形。

plot3d ()函数的用法如下：

plot3d(expr, range\_x, range\_y, \*\*kwargs)

一张画布显示同范围多图

plot3d(expr1, expr2, range\_x, range\_y, \*\*kwargs)

一张画布显示不同范围多图

plot3d((expr1, range\_x, range\_y), (expr2, range\_x, range\_y), ..., \*\*kwargs)

例：画出函数的图像，其中

\[]:from sympy.plotting import plot3d

\[]:plot3d(x\*\*2\*y, (x, -5, 5), (y, -5, 5))

![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\9C87D10B.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-5f79c8e63204794273e19fb2b71cd5500f860384%2F482cef54c33b1ac8fc4174fed4ef7476.png?alt=media)

\[]:\<sympy.plotting.plot.Plot at 0x17364eb8>

例：画出函数与的组合图像，其中

\[]:plot3d(x\*\*2+y\*\*2, x\*\*2-y\*\*2, (y, -5, 5))

![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\DC4A5A51.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-63229f03da438deeaa32d7cfc5293bbd4731c628%2F451d310dce0db1ed6fbda1872f500b42.png?alt=media)

\[]:\<sympy.plotting.plot.Plot at 0x17238e10>

**参数方程**

**空间曲线**

使用plot3d\_parametric\_line函数可以绘制参数方程代表的三维图形。

plot3d\_parametric\_line的用法如下：

单图

plot3d\_parametric\_line(expr\_x, expr\_y, expr\_z, range\_u, range\_v, \*\*kwargs)

一张画布显示不同范围多图

plot3d\_parametric\_line((expr\_x, expr\_y, expr\_z, range\_u, range\_v), ..., \*\*kwargs)

例：绘制螺旋线

\[]:from sympy.plotting import plot3d\_parametric\_line

\[]:plot3d\_parametric\_line(x,sin(6\*x)\*x/3,cos(6\*x)\*x/5,(x,0,6\*pi))

![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\2D800C2D.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-54e3eeca9b5eedd3bd5c58a2362b6aadd9ae6093%2Ffc6deada89d8ddfc87f3abea58c0a723.png?alt=media)

\[]:\<sympy.plotting.plot.Plot at 0x18f410f0>

**空间曲面**

使用plot3d\_parametric\_surface函数可以绘制参数方程代表的三维图形。

plot3d\_parametric\_surface的用法如下：

单图

plot3d\_parametric\_surface(expr\_x, expr\_y, expr\_z, range\_u, range\_v, \*\*kwargs)

一张画布显示不同范围多图

plot3d\_parametric\_surface((expr\_x, expr\_y, expr\_z, range\_u, range\_v), ..., \*\*kwargs)

例：绘制的图像。

\[]:from sympy.plotting import plot3d\_parametric\_surface

\[]:plot3d\_parametric\_surface(cos(x + y), sin(x - y), x - y, (x, -5, 5), (y, -5, 5))

\[]:![C:\Users\Johan\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\AC5AEA35.tmp](https://914753012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mjx-LkXo6sfWzVc3cno%2Fuploads%2Fgit-blob-fd3610c677418c353dbe442a48bb92b893079c84%2F8787289a54358dd62fbba28d8e829951.png?alt=media)

\[]:\<sympy.plotting.plot.Plot at 0xbe42550>
