案例:绘制Matplotlib动态图

2022-06-24 18:45:52 浏览数 (1)

学习 zhenguo 老师的 Python 课已经一个星期了,自己感觉已经学有小成,刚好昨天老师在接单群里发了一个 100元的单子,我毫不犹豫的接了,不仅可以检验自己能否学以致用,还能赚顿小龙虾的钱(50元~)。

开发需求

这个单子的要求,是使用 Python 中的 matplotlib 库绘制动态的折线图,需求描述虽然很简单易懂,但是也要好好分析一下。

Matplotlib库

这个库也算是 Python 数据开发必学的库之一了,它主要的功能就是绘制图表,而且实现也非常简单,几行代码就可以绘制出直方图、折线图、散点图、饼图等等常用的图表,一些复杂的数据分析图表它也可以胜任。

这里分享一个网址: www.matplotlib.org.cn/gallery/#lines-bars-and-markers,大家可以看看 Matplotlib 绘图的一些案例,作为自己开发的参考。

核心问题

Matplotlib 库绘制一张静态的折线图比较简单,给定X轴和Y轴的数据集就行,但是想要绘制动态的折线图,就要想办法让绘制出来的图片动起来。

其实这个问题理解起来也不难,Matplotlib 绘图是生成一张图片,让它动起来的原理就像是动画片,不断生成新的图片,让它们前后连接,逐帧播放就行了。

开发过程

理解了核心问题,就可以开始动手来解决问题了。

加载数据

在绘图之前,先要把数据集合弄到,需求方给了一张 excel 表格,需要从表格中提取需要的数据集。

这个过程也是比较简单的,需要用到 Python 数据分析必学的另一个库 Pandas

只需要一行代码 pandas.read_excel('data.xls'),就可以把 excel 文件加载到内存。

然后可以像操作 dict 一样获取每一列的数据集合,如:cls = pandas.read_excel('data.xls')['列头名称'],会得到一个 <class 'pandas.core.series.Series'> 对象,可以直接遍历每行的数据,也可以格式转化成列表类型。

绘制一条折线

有了数据之后,就可以画图了,先画一条折线,把现有的数据展示出来,看看效果。

代码语言:javascript复制
import matplotlib.pyplot as plt
import data

fig, ax = plt.subplots(figsize=(16, 9), dpi=80)
ax.set_ylim(0, 35)
ax.set_xticks(range(0, len(xdata), 10))

xdata = data.times
ydata = data.dealnums
ax.plot(xdata, ydata)

fig.autofmt_xdate()
plt.show()

代码比较简单,下面是绘制出来的折线图:

让折线动起来

接下来要做的,就是要让折线图动起来,不断显示新的数据。

要实现这个效果,需要做两个操作,一是让数据动起来,在数据集中不断增加新数据,二是让绘制的图形按指定时间间隔动起来。

第一个操作,我是这样做的,直接上代码:

代码语言:javascript复制
for x, y in zip(xdata_set, ydata_set):
    xdata.append(x)
    ydata.append(y)

也就是将原始数据集拆成单个数据,逐个加载到X轴和Y轴的数据集中,实现数据动态增加的效果。

第二个操作,我首先想到的办法,是每次数据更新的同时,将整个画布清空,重新画出最新的图表。

代码语言:javascript复制
for x, y in zip(xdata_set, ydata_set):
    xdata.append(x)
    ydata.append(y)
    
    plt.clf()  # 清空整个figure
    
    # 重新建立坐标轴并画出折线图
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(xdata, ydata)
    
    plt.pause(1) # 休眠1秒后绘制新图

这个思路还有一点需要注意,就是要在开始调用 plt.ion() 方法,启动互动模式。

这个方式不能直接保存图像为动图,所以没法展示了,效果跟后面的动图一样。

动图新思路

按照上面的思路完成各个需求细节之后,我就把代码提交给了 zhenguo 老师,老师不仅给予了肯定和鼓励,还提供了一个新的思路。

也就是使用 Matplotlib 中的动画模块来画动态图。

赶紧找到相关模块和方法的文档学习了起来,最后发现,只需要使用一个 animation.FuncAnimation 类就可以满足这个单子的需求。

而且这个类用起来很方便,在构建函数中传入 figure 对象、更新图表的函数、初始化函数和间隔参数就行了。

代码语言:javascript复制
import matplotlib.animation
import matplotlib.pyplot as plt
import data

fig = plt.figure(figsize=(16, 9), dpi=70)

def draw_line(fig, x_data, y_data):
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(xdata, ydata)
    return ax

def init_figure():
    # 绘制初始的图表
    xdata = data.times
    ydata = data.dealnums

    return draw_line(fig, xdata, ydata)
    
def update(n):
    # 更新数据集 xdata 和 ydata
    xdata = data.new_times
    ydata = data.new_dealnums
    # 清除之前的坐标系
    plt.clf() 
    # 绘制最新的折线图
    return draw_line(fit, xdata, ydata)

ani = animation.FuncAnimation(fig, update, init_func=init_figure, interval=1000)

# 生成 gif 动图并保存
ani.save('test.gif', writer='pillow')

plt.show()

这样改造之后的代码也是非常简单清晰,而且还能保存生成的动图。

最后完善一些需求和代码上的细节问题,最终的效果是这样的:

是不是很酷!

学习了 Python 之后,发现了很多有意思的编程方向,绘图开发只是宏观蓝图的一小部分。

通过这次的单子,我完成了从眼会到手会的突破,不仅对 Matplotlib 库有了更深的理解,更重要的是!

今晚的小龙虾有着落了!

0 人点赞