Reader

【Python】科学绘图库Matplotlib

| dtysky|一个行者的轨迹 | Default

这是一个Python的科学绘图库的使用心得,涉及平面绘制、子图绘制、格点、三维(3D)绘制以及动态绘图。
http://matplotlib.org/

基本绘制

直接导入库后,提供数组进行绘制即可:

基础

import matplotlib.pyplot as plt
x = [1, 2, 3]
y = [1, 2, 3]
# 绘制线图
plt.plot(x, y, color, ...)
# 绘制散点图
plt.scatte()
plt.show()

"基本二维图"

还可以绘制别的图像,详见官网。

进阶

子图

可以添加多张子图进行绘制,并且修改图像的标题和坐标轴等:

plt.subplot(2, 2, 1)
plt.title("子图像1")
plt.subplot(2, 2, 2)
plt.title("子图像2")
plt.subplot(2, 2, 3)
plt.title("子图像3")
plt.subplot(2, 2, 4)
plt.title("子图像4")

"基本二维图"

面向对象

除了这种类似于matlab的用法,还可以用面向对象的用法来进行绘制:

import matplotlib.pyplot as plt
fig = plt.figure()
axi = fig.add_subplot(2, 2, 1)
axi.set_title("子图1")
axi.set_xlabel("x轴")
axi.set_ylabel("y轴")
ax.set_xlim(min(0, 10)
ax.set_ylim(0, 10)
......

细化分隔

也可以进一步细化控制每一个子图所占区域范围:

import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(2, 2)
fig = plt.figure(1, figsize=(16, 9), dpi=72)
fig.suptitle("Distance offset from start")
ax1 = fig.add_subplot(gs[:, 0])
ax1.set_title("Total")
ax1.set_xlabel("X(m)")
ax1.set_ylabel("Y(m)")
ax2 = fig.add_subplot(gs[0, 1])
ax2.set_title("X")
ax2.set_xlabel("t(s)")
ax2.set_ylabel("0m")
ax3 = fig.add_subplot(gs[1, 1])
ax3.set_title("Y")
ax3.set_xlabel("t(s)")
ax3.set_ylabel("0m")

"基本二维图"

这种情况下,第一张子图将占据第一行,第二张子图占据第二行的第一列,第三张则是第二行的第二列。

三维

三维图的绘制也是被支持的,只需要导入一个模块便可以在绘制的时候加上z轴的参数:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(1, figsize=(16, 9), dpi=72)
ax1 = fig.add_subplot(1, 1, 1, projection="3d")
ax1.set_title("三维图")
ax1.set_xlabel("X")
ax1.set_ylabel("Y")
ax1.set_zlabel("Z")
ax1.scatter([1, 2, 3], [1, 2, 3], [1, 2, 3])

"基本二维图"

动画

动画也是被支持的,其核心在于一个模块和一个方法,以下是我常用的方案更详细可以看这里

导入核心库和使用核心方法:

# 核心库
import matplotlib.animation as animation
# 生成动画对象
ani = animation.FuncAnimation(fig_p, draw_distances_run, draw_distances_data_gen, interval=200)

其中draw_distances_run方法是绘制每一帧更新时用于绘制的方法,draw_distances_data_gen方法用于在绘制中修改绘制数据,而interval参数则是一个毫秒的时间,是每两帧之间的间隔。 那么如何具体的来使用呢?

我此处选择的是一个并非那么合理但是快速的方法,当然,其实外面用个类包起来其实就好,满足需求就行。。。

初始化数据

def draw_distances_start():
    fig = plt.figure(1, figsize=(16, 9), dpi=120)
    fig.suptitle("Distance offset from start")
    ax1 = fig.add_subplot(1, 1, 1, projection="3d")
    ax1.set_title("图1")
    ax1.set_xlabel("X")
    ax1.set_ylabel("Y")
    ax1.set_zlabel("Z")
    data1 = ax1.scatter([], [], [])
    return fig, ax1, data1
# 全局变量
fig, ax1, data1 = draw_distances_start()
data_now = ["x": [], "y": [], "z": []]
data_new = ["x": [], "y": [], "z": []]

更新数据

def draw_distances_data_gen():
    for key in data_now:
        data_now[key].extend(data_new[key])
    yield data_now

更新绘制

def draw_distances_run(d):
    x, y, z = d["x"], d["y"], d["z"]
    data1.set_offsets([(x[-1], y[-1])])
    data1.set_3d_properties([z[-1]], "z")
    ax1.set_xlim(min(x), max(x))
    ax1.set_ylim(min(y), max(y))
    ax1.set_zlim(min(z), max(z))
    return data1

注意,这里更新x和y用的是set_offsets方法,而更新z用的是set_3d_properties方法。

提供新数据

以上代码中,一个全局的data_now变量用于存储当前要绘制的数据,而data_new则是该帧的新数据。在实际使用中,为了防止卡顿,一般选择用一个新的线程来更新data_new的数据:

def refresh():
    while True:
        data_new = get_new_data()

thread.start_new(refresh, ())

一切准备就绪后,调用plt.show()即可开始绘制动画。