前言

Matplotlib 在 Python 中提供了类似于 MATLAB 的绘图支持,有人说使用 NumpyPandasScipyMatplotlib 就可以完全替代 MATLAB。其中,Matplotlib 提供图像库支持,而 Numpy 提供优秀的向量计算、Pandas 提供便携的结构存储、Scipy 提供额外的计算库支持。Matplotlib 经常被用于数据分析,常常和 Numpy 等一起使用,本文不会系统的介绍这些库,而是从一些示例的方式快速入手 Matplotlib。

在阅读本文方面的建议是,你无需记忆每个模块,需要时将本文中的代码粘贴到你的 IDE 中然后尝试运行并根据你的需求改写。另外,提前声明作者本人并未使用过 MATLAB 而直接学习使用的 Matplotlib。

多样式平面折线

Matplotlib 绘图最常用的就是 matplotlib.pyplot.plot 方法,它接受主参数 xdatax_{data}ydatay_{data},表示对应的 xx 坐标和 yy 坐标,例如 (xdata[0], ydata[0])(x_{data}[0],~y_{data}[0]) 表示第一个坐标点。另外,plot 提供一些辅参数,用于描述坐标点样式或坐标点连接样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
import matplotlib.pyplot as plt

x_data = np.array([1, 2, 3])
y_data = np.array([1, 4, 9])

# 绘制
plt.plot(x_data, y_data, 'go--', label='An example spline') # 使用 fmt 格式化样式
# plt.plot(x_data, y_data, color='g', marker='o', linestyle='--', label='An example spline') # 分别格式化样式
# g: 深绿色 (可以用 #XXXXXX 表示 RGB 颜色), o: 实心圆坐标点, --: 虚线坐标连线, label 用于在显示图例时显示

# 添加标签和图例, 可以使用 Latex 公式哦
plt.xlabel('x') # x 轴标签
plt.ylabel('y') # y 轴标签
plt.title('An example figure') # 图表标题
plt.legend() # 添加图例

# 显示图表
plt.savefig('fig1.png') # 保存图片, 使用 plt.figure(dpi=100) 修改值, 设置分辨率和图像尺寸
plt.show() # 显示图片 (阻塞地)

下面是全部样式展示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import numpy as np
import matplotlib.pyplot as plt

# 全局设置, 为了更好的展现效果
plt.rcParams['axes.facecolor'] = '#EEEEEE' # 浅灰背景
plt.rcParams['font.family'] = 'monospace' # 等线字体

x_data = np.array([1, 2, 3])
y_data = np.array([1, 4, 9])

# 样式展示, 这里列举的样式与图标中从上到下一一对应
# 这边只提供了一些示例, 可自行对样式排列组合
style_example = {
'bo-': 'blue color, circle marker, solid linestyle',
'g.--': 'green color, point marker, dash linestyle',
'r,-.': 'red color, pixel marker, dash-dot linestyle',
'cv:': 'cyan color, tri down (filled) marker, dot linestyle',
'm^': 'magenta color, tri up (filled) marker, none linestyle',
'y<-': 'yellow color, tri left (filled) marker, solid linestyle',
'k>--': 'black color, tri right (filled) marker, dash linestyle',
'w1-.': 'white color, tri down marker, dash-dot linestyle',
'b2:': 'blue color, tri up marker, dot linestyle',
'g3': 'green color, tri left marker, none linestyle',
'r4-': 'red color, tri right marker, solid linestyle',
'c8--': 'cyan color, octagon marker, dash linestyle',
'ms-.': 'magenta color, square marker, dash-dot linestyle',
'yp:': 'yellow color, pentagon marker, dot linestyle',
'k*': 'black color, star marker, none linestyle',
'wh-': 'white color, hexagon (vertical) marker, solid linestyle',
'bH--': 'blue color, hexagon (horizontal) marker,dash linestyle',
'g+-.': 'green color, plus marker, dash-dot linestyle',
'rP:': 'red color, plus (filled) marker, dot linestyle',
'cx': 'cyan color, x marker, none linestyle',
'mX-': 'magenta color, x (filled) marker, solid linestyle',
'yD--': 'yellow color, diamond marker, dash linestyle',
'kd-.': 'black color, diamond (thin) marker, dash-dot linestyle',
'w|:': 'white color, vline marker, dot linestyle',
'_': 'auto color, hline marker, none linestyle', # 不设置颜色, 会自动分配颜色
}

# 绘制
for i, key in enumerate(style_example):
plt.plot(x_data, y_data + len(style_example) - i, key, label=style_example[key])

# 添加标签和图例, 可以使用 Latex 公式哦
plt.xlabel('x') # x 轴标签
plt.ylabel('y') # y 轴标签
plt.title('An example figure') # 图表标题
plt.legend() # 添加图例

# 显示图表
plt.savefig('fig2.png') # 保存图片
plt.show() # 显示图片 (阻塞地)

最后,matplotlib.pyplot.plot 的文档写的十分详细:

1
2
import matplotlib.pyplot as plt
print(help(plt.plot)) # 查看帮助文档

绘制一元函数曲线

也许初学者会感到和认知有些出入,Matplotlib 只能通过传入样点绘制图像,那么如何绘制函数图像呢?其实很简单,样点足够多它就(看起来像)是一条曲线了。样点一般取 100 个即可。当然样点必须处理好后传入 Matplotlib 库,这里我们简单的用到了一些 Numpy 的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
import matplotlib.pyplot as plt

def f(x):
return x ** 2 - 3 * x + 2

# 生成样点
x_data = np.linspace(0, 5, 100) # 范围均匀取点
y1_data = np.sin(x_data) # 向量正弦运算
y2_data = f(x_data) # 自定义向量运算

# 绘制
plt.plot(x_data, y1_data, label='sin(x)')
plt.plot(x_data, y2_data, label=r'$x^2-3x+2$') # LATEX 公式

# 添加标签和图例
plt.xlabel('x') # x 轴标签
plt.ylabel('y') # y 轴标签
plt.title('A figure of sin(x) and a quadratic function') # 图表标题
plt.legend() # 添加图例

# 显示图表
plt.savefig('fig1.png') # 保存图片
plt.show() # 显示图片 (阻塞地)

单图多表的绘制

也许你需要在一个页面中同时显示多个图表,这个问题当然也可以解决。我们将使用 matplotlib.pyplot.subplots 解决这个问题。

在使用 subplots 中也有几点需要注意:

  • subplots 传入两个参数 nrows / ncols,当其中一个参数为 1 时,其返回的 axes 是一维对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    _, axes = plt.subplots(3, 1)  # 创建 3x1 的子图表系
    axes[2] # 索引 2 行 0 列的子图表
    # axes[2][0] # 错误地, axes 是一维对象

    _, axes = plt.subplots(1, 3) # 创建 1x3 的子图表系
    axes[2] # 索引 0 行 2 列的子图表
    # axes[0][2] # 错误地, axes 是一维对象

    _, axes = plt.subplots(3, 2) # 创建 3x1 的子图表系
    axes[2][0] # 正确地, axes 是二维对象

    # 很奇怪? 我也觉得很奇怪...
  • axes 对象的常用方法集有 plot / set_xlabel / set_ylabel / set_title / legend;而 plt 的常用方法集有 plot / xlabel / ylabel / title / legend,注意区分。

下面是一个示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from math import pi
from fractions import Fraction # 内置分数类

import numpy as np
import matplotlib.pyplot as plt

# 生成样点
x_data = np.linspace(-10, 10, 100)

# 生成子图表集
fig, axes = plt.subplots(3, 2)

# 绘制子图
for i in range(3):
frac = Fraction(i, 6)
axes[i][0].plot(
x_data,
np.sin(x_data + pi * float(frac)),
)
axes[i][1].plot(
x_data,
np.cos(x_data + pi * float(frac)),
)
axes[i][0].set_xlabel('x')
axes[i][0].set_ylabel('y')
axes[i][0].set_title(r'$Figure~of~\sin(x{})$'.format(
r'+\frac{{{}}}{{{}}}\pi'.format(*frac.as_integer_ratio()) if frac else ''
)) # 我写的这个 format 确实有点一坨
axes[i][1].set_xlabel('x')
axes[i][1].set_ylabel('y')
axes[i][1].set_title(r'$Figure~of~\cos(x{})$'.format(
r'+\frac{{{}}}{{{}}}\pi'.format(*frac.as_integer_ratio()) if frac else ''
))

# 自动调整图间距, 否则图标会重合
plt.tight_layout()

# 显示图形
plt.savefig('fig1.png')
plt.show()

双纵坐标的绘制

有时两个函数具有不同单位,或者两者是数量级的差距时,使用常规的坐标系无法呈现好的效果。下面提供我们将绘制一个双纵坐标,并与不使用双纵坐标对比。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import numpy as np
import matplotlib.pyplot as plt

# 生成样点
x_data = np.linspace(0, 10, 100) # 范围均匀取点
y1_data = np.sin(x_data) # 向量正弦运算
y2_data = np.exp(x_data) # 自定义向量运算

# 生成子图表集
fig, axes = plt.subplots(2)

# 绘制单纵坐标图表
axes[0].plot(x_data, y1_data, label='sin(x)')
axes[0].plot(x_data, y2_data, label='exp(x)')
axes[0].set_xlabel('x') # x 轴标签
axes[0].set_ylabel('y') # y 轴标签
axes[0].set_title('A bad example') # 图表标题
axes[0].legend() # 添加图例

# 绘制双纵坐标图表
axes[1].plot(x_data, y1_data, color='#1F77B4', label='sin(x)')
axes[1].set_xlabel('x') # x 轴标签
axes[1].set_ylabel('y') # y 轴标签
axes_twin = axes[1].twinx()
axes_twin.plot(x_data, y2_data, color='#FF7F0F', label='exp(x)')
axes_twin.set_xlabel('x') # x 轴标签
axes_twin.set_ylabel('y') # y 轴标签
axes[1].set_title('Maybe a good example') # 图表标题
axes[1].legend() # 添加图例
axes_twin.legend()

# 显示图表
plt.tight_layout()
plt.savefig('fig1.png')
plt.show()

离散点函数拟合

在实验时,我们经常需要将离散点拟合成函数。例如,在我的物理实验课程中,就被要求绘制关系图像。当然实验并不要求使用计算机绘制图像,但有强迫症的我真的很难受。

  • 线性拟合
  • 完美拟合(高阶多项式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import numpy as np
import matplotlib.pyplot as plt

# 这是我在物理实验的光电效应中的数据
x = np.array([8.214, 7.408, 6.879, 5.490, 5.196])
y = np.array([-1.922, -1.545, -1.329, -0.785, -0.674])

# 线性拟合, 用一阶多项式拟合
linear_fit = np.polyfit(x, y, 1) # 返回系数序列, 从高位到低位
linear_fit_fn = np.poly1d(linear_fit) # 转为一元 numpy 函数
print(linear_fit)

# 完美拟合, 用高阶多项式拟合, 当然光电效应实验应该使用线性拟合
perfect_fit = np.polyfit(x, y, x.size // 2) # 第 3 个参数为多项式阶数, 阶数高不一定效果好
perfect_fit_fn = np.poly1d(perfect_fit) # 转为一元 numpy 函数
print(perfect_fit)

# 绘制
plt.scatter(x, y, label='Data') # 绘制原始数据点
xfit = np.linspace(min(x), max(x), 100) # 生成样点
plt.plot(xfit, linear_fit_fn(xfit), label=r'{:.3f}$\nu{:+.3f}$'.format(*linear_fit), linestyle='--') # 绘制线性拟合曲线
plt.plot(xfit, perfect_fit_fn(xfit), label='Perfect Fit', linestyle=':') # 绘制完美拟合曲线

# 添加标签和图例
plt.xlabel(r'$\nu(\times 10^{14}Hz)$')
plt.ylabel(r'$U_0(V)$')
plt.title(r'$U_0-\nu~Graph$')
plt.legend()

# 显示图形
plt.savefig('fig1.png') # 保存图片
plt.show()

  • 平滑拟合(数据插值)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import UnivariateSpline # 一元曲线拟合类

# 这是我在物理实验中测量磁极附近磁场分布的数据
x_data = np.linspace(-4.0, 4.0, 17)
y_data = np.array([2.6, 3.3, 4.3, 5.8, 8.4, 13.4, 24.2, 38.8, 43.0, 38.8, 24.2, 13.5, 8.4, 5.7, 4.3, 3.3, 2.6])

# 平滑拟合
spline = UnivariateSpline(x_data, y_data, s=0) # s 设置平滑度,越小越平滑
x_smooth = np.linspace(min(x_data), max(x_data), 100) # 生成样点
y_smooth = spline(x_smooth) # 返回对象可作仿函数使用, 支持 numpy 数组

# 绘制
plt.scatter(x_data, y_data, label='Data')
plt.plot(x_smooth, y_smooth, label='Smoothed Fit', linestyle='--')

# 添加标签和图例
plt.xlabel(r'$\Delta{x}(cm)$')
plt.ylabel(r'$B(mT)$')
plt.title(r'$B-\Delta{x}~Graph~(I_M=500mA)$')
plt.legend()

# 显示图形
plt.savefig('fig2.png') # 保存图片
plt.show()

绘制频率直方图

频率直方图用于以柱状图的形式描绘样本中不同值出现的频率等信息。这里我们可以使用 matplotlib.pyplot.hist 直接绘制频率直方图,也可以使用 numpy.histogram 得到频率统计数据然后做进一步处理。

  • matplotlib.pyplot.hist 绘制直方图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
import matplotlib.pyplot as plt

# data = np.array(...)

plt.hist(
data, # 原始样本数据
bins=30, # 统计直方数
density=True # 是否计算百分比
)

# 添加轴标签和标题
plt.xlabel('Average Run Distance in each Game')
plt.ylabel('Frequency')
plt.title('Run Frequency of Player Carlos Alcaraz')

# 显示图形
plt.show()

  • numpy.histogram 绘制频率曲线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import UnivariateSpline

# data = np.array(...)

# 统计频率并拟合曲线
counts, edges = np.histogram(data, bins=30, density=True) # 获取频率统计数据
spline = UnivariateSpline(edges[:-1], counts, s=0) # 适配拟合器
x_smooth = np.linspace(min(edges[:-1]), max(edges[:-1]), 1000) # 生成样点
y_smooth = spline(x_smooth) # 获取拟合值
plt.plot(x_smooth, y_smooth) # 绘制频率拟合曲线

# 添加轴标签和标题
plt.xlabel('Average Run Distance in each Game')
plt.ylabel('Frequency')
plt.title('Run Frequency in each Game')

# 显示图形
plt.show()

绘制二元函数热力图

绘制二元函数时,使用热力图是很常用的方法。它很多时候比三维图更加直观,当然我们后面也会介绍三维图的绘制。在二元函数绘制时,我们可能需要使用 Numpy 广播机制,另外将使用的 matplotlib.pyplot.imshow 接口比较特殊,它自动生成等差二元样点,而我们只需在 extend 参数传入 xxyy 的上下界即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from math import pi

import numpy as np
import matplotlib.pyplot as plt

def f(x, y):
return np.sin(x) * np.cos(y)

x_data = np.linspace(-pi, pi, 100)
y_data = np.linspace(-pi, pi, 100)
x_grid = np.broadcast_to(x_data.reshape((100, 1)), (100, 100)) # Numpy 广播机制
y_grid = np.broadcast_to(y_data.reshape((1, 100)), (100, 100))
# x_grid, y_grid = np.meshgrid(x_data, y_data) # 网格化, 上面两行也可用该语句替换
z_data = f(x_grid, y_grid) # 生成函数样点

# 绘制热力图
plt.imshow(
np.flip(z_data.transpose(), axis=0), # b
extent=(
min(x_data), # x 轴左界
max(x_data), # x 轴右界
min(y_data), # y 轴下界
max(y_data)), # y 轴上界
aspect=(max(x_data) - min(x_data)) / (max(y_data) - min(y_data)), # 拉伸为正方形
cmap='rainbow', # 颜色样式: 彩虹
# cmap='viridis', # 颜色样式: 翠绿(default)
# cmap='gray', # 颜色样式: 灰度
)

# 添加颜色条
plt.colorbar(label='Function Value')

# 添加轴标签
plt.xlabel('x')
plt.ylabel('y')
plt.title('Heatmap of a 2D Function')

# 显示图形
plt.savefig('fig1.png')
plt.show()

绘制二元函数等高图

绘制等高图与热力图功能和用法相似,并且它们使用了相同的 cmap 颜色样式。另外,我们还可以通过 contour 换成 contourf 得到另一种填充样式的登高图,这展示在我们最后的两张图像的对比中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from math import pi

import numpy as np
import matplotlib.pyplot as plt

def f(x, y):
return np.sin(x) * np.cos(y)

x_data = np.linspace(-pi, pi, 100)
y_data = np.linspace(-pi, pi, 100)
x_grid = np.broadcast_to(x_data.reshape((100, 1)), (100, 100)) # Numpy 广播机制
y_grid = np.broadcast_to(y_data.reshape((1, 100)), (100, 100))
# x_grid, y_grid = np.meshgrid(x_data, y_data) # 网格化, 上面两行也可用该语句替换
z_data = f(x_grid, y_grid) # 生成函数样点

# 绘制等高图
contour = plt.contour( # contour 为标准等高图
# contour = plt.contourf( # contourf 变填充等高图
x_grid,
y_grid,
z_data,
levels=30, # 等高线总数
cmap='gray_r', # 颜色样式: 灰度 (转向)
# 颜色样式中 _r 表示对样式的转向 (如小黑大白的转向是小白大黑)
)
# 绘制等高线标签
plt.clabel(
contour, # 等高图对象
inline=True, # True: 中断等高线 / False: 悬于等高线
fontsize=8,
colors='k',
fmt='%1.1f', # 格式化字符串
)

# 添加颜色条
plt.colorbar(label='Function Value')

# 添加轴标签
plt.xlabel('x')
plt.ylabel('y')
plt.title('Contour map of a 2D Function')

# 显示图形
plt.savefig('fig1.png')
plt.show()

三维图表绘制

我个人并不推荐使用 Matplotlib 绘制三维图表,一是其导出的二维图片无法展现全部信息,二是其绘制的三维曲面效果一般。如果想要绘制二元函数,建议使用上面的介绍的 绘制二元函数热力图 / 绘制二元函数登高图。当然在可交互的情况下,Matplotlib 也可以展示直观的三维效果。

  • 三维曲线绘制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import matplotlib.pyplot as plt

# 生成样点, 样点必须是一维的
t_data = np.linspace(0, 100, 1000) # 注意, 这时 100 个样点就不显得足够了

# 绘制螺线形
x_data = np.sin(t_data)
y_data = np.cos(t_data)
z_data = t_data / 100

# 生成子图集
fig = plt.figure()
axes = fig.add_subplot(projection='3d')

# 绘制子图
axes.plot(x_data, y_data, z_data, '-')

# 显示图形
plt.savefig('fig1.png')
plt.show()

  • 三维曲面绘制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from math import pi

import numpy as np
import matplotlib.pyplot as plt

def f(x, y):
return np.sin(x) * np.cos(y)

# 生成样点, 样点必须是一维的
x_data = np.broadcast_to(np.linspace(-pi, pi, 100).reshape((100, 1)), (100, 100)).reshape(10000)
y_data = np.broadcast_to(np.linspace(-pi, pi, 100).reshape((1, 100)), (100, 100)).reshape(10000)
z_data = f(x_data, y_data)

# 生成子图集
fig = plt.figure()
axes = fig.add_subplot(projection='3d')

# 绘制子图
axes.plot(x_data, y_data, z_data, '.', alpha=0.5)
# 设置样式描点不连线, 设置不透明度 0.5, 否则无法展示被遮挡部分

# 显示图形
plt.savefig('fig2.png')
plt.show()

动图绘制

  • 二维动图绘制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# 生成样点
# 我将绘制一个流动的高斯噪音, 粘贴到你的 IDE 尝试运行
x_data = np.linspace(0, 10, 100)
y_data = np.random.normal(0, 1, 100) # 高斯噪音

# 绘制
fig, axes = plt.subplots(1, 1)
sample, = axes.plot(x_data, y_data) # 接收样点系, 在动画中修改. 注意返回的数组, 需要解包

# 设置动态回调
def callback(frame):
global y_data
y_data = np.hstack((y_data[1:], np.random.normal(0, 1, 1)))
sample.set_data(x_data, y_data) # 设置下一帧画面
return sample

# 设置动态加载
ani = FuncAnimation(
fig,
callback, # 回调函数
frames=1, # 帧数, 将从 [0, frames) 循环传入
interval=50, # 两帧间时间间隔 (ms)
) # 如果不接收返回值该语句会被忽略

# 添加标签和图例
plt.xlabel('x') # x 轴标签
plt.ylabel('y') # y 轴标签
plt.title('A Gaussian Noise Wave') # 图表标题

# 显示图表
plt.savefig('fig1.png') # 保存图片
plt.show() # 显示图片 (阻塞地)

  • 三维动画绘制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from math import pi

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# 生成样点
# 我将绘制一个绕外部中心公转并沿z自转的牛角螺线形, 粘贴到你的 IDE 尝试运行
t_data = np.linspace(0, 2, 1000) # 注意, 这时 100 个样点就不显得足够了

# 绘制牛角螺线形
x_data = np.sin(t_data) * (1 + np.sin(t_data * 50) * t_data / 6)
y_data = np.cos(t_data) * (1 + np.sin(t_data * 50) * t_data / 6)
z_data = np.cos(t_data * 50) * t_data / 6

# 生成子图集
fig = plt.figure()
axes = fig.add_subplot(projection='3d')

# 绘制子图
axes.set_xlim(-1.5, 1.5) # 设置 x 坐标范围
axes.set_ylim(-1.5, 1.5) # 设置 y 坐标范围
axes.set_zlim(-1, 1) # 设置 z 坐标范围
sample, = axes.plot(x_data, y_data, z_data, '-') # 接收样点系, 在动画中修改. 注意返回的数组, 需要解包
axes.set_title('Spinning Ox Horn Spiral Line')

# 设置动态回调
def callback(frame):
global x_data, y_data, z_data
_delta = frame / 100 * 2 * pi # 这样刚好 100 frame 一循环
x_data = np.sin(t_data + _delta) * (1 + np.sin((t_data + _delta) * 50) * t_data / 6)
y_data = np.cos(t_data + _delta) * (1 + np.sin((t_data + _delta) * 50) * t_data / 6)
z_data = np.cos((t_data + _delta) * 50) * t_data / 6
sample.set_data(x_data, y_data)
sample.set_3d_properties(z_data)
return sample

# 设置动态加载
ani = FuncAnimation(fig, callback, frames=100, interval=50)

# 显示图形
plt.savefig('fig2.png')
plt.show()

尝试使用上面的方法,构造并绘制一个稳定的三体运动(出自科幻小说《三体》)。

中文显示问题

这个问题其实只要设置中文字体即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import numpy as np
import matplotlib.pyplot as plt

# 设置中文显示
plt.rcParams['font.family'] = 'Microsoft YaHei' # 设置字体为微软雅黑
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题

def f(x):
return x ** 2 - 3 * x + 2

# 生成样点
x_data = np.linspace(0, 5, 100) # 范围均匀取点
y1_data = np.sin(x_data) # 向量正弦运算
y2_data = f(x_data) # 自定义向量运算

# 绘制
plt.plot(x_data, y1_data, label='sin(x)')
plt.plot(x_data, y2_data, label=r'$x^2-3x+2$') # LATEX 公式

# 添加标签和图例
plt.xlabel('x 轴') # x 轴标签
plt.ylabel('y 轴') # y 轴标签
plt.title(' sin(x) 和一个二元函数的图像') # 图表标题
plt.legend() # 添加图例

# 显示图表
plt.savefig('fig1.png') # 保存图片
plt.show() # 显示图片 (阻塞地)

你可以使用下面的命令查看系统支持的字体。

1
2
3
import matplotlib.font_manager
installed_fonts = [font.name for font in matplotlib.font_manager.fontManager.ttflist]
print(installed_fonts)