案例:绘制Matplotlib动态图
学习 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'>
对象,可以直接遍历每行的数据,也可以格式转化成列表类型。
绘制一条折线
有了数据之后,就可以画图了,先画一条折线,把现有的数据展示出来,看看效果。
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()
代码比较简单,下面是绘制出来的折线图:
让折线动起来
接下来要做的,就是要让折线图动起来,不断显示新的数据。
要实现这个效果,需要做两个操作,一是让数据动起来,在数据集中不断增加新数据,二是让绘制的图形按指定时间间隔动起来。
第一个操作,我是这样做的,直接上代码:
for x, y in zip(xdata_set, ydata_set):
xdata.append(x)
ydata.append(y)
也就是将原始数据集拆成单个数据,逐个加载到X轴和Y轴的数据集中,实现数据动态增加的效果。
第二个操作,我首先想到的办法,是每次数据更新的同时,将整个画布清空,重新画出最新的图表。
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
对象、更新图表的函数、初始化函数和间隔参数就行了。
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 库有了更深的理解,更重要的是!
今晚的小龙虾有着落了!