使用matplotlib.animation从CSV实时绘图:数据连接至首个条目错误
问题原因分析
你遇到的额外线条问题,核心原因在于全局变量x、y1、y2、y3在每次动画更新时没有被清空,且每次都会重新读取整个CSV文件的所有数据并追加到列表中。
举个实际场景的例子:
- 第一次调用
animate函数时,读取了CSV里的现有数据,列表里存储了第1条数据; - 第二次调用(1秒后,CSV新增了第2条数据),你的代码会再次读取整个CSV的所有数据(第1+第2条),并把这两条数据追加到已经存着第1条数据的列表里。此时
x变成[time1, time1, time2],y1变成[v1, v1, v2]; - 当
ax1.plot(x,y1)执行时,会按顺序连接所有点:time1→v1→time1→v1→time2→v2。下一次动画更新时,列表又会追加第1+第2+第3条数据,最终导致绘图时出现从最新数据点跳回首个数据点的多余线条——因为列表里重复的旧数据会被重复绘制。
修复方案
有两种简单的修复方式,推荐第一种(更清晰,避免全局变量的副作用):
方案1:在animate函数内部初始化列表
把x、y1等列表的定义放到animate函数内部,这样每次调用函数都会创建全新的空列表,读取最新的CSV数据填充,不会有旧数据残留:
import matplotlib.pyplot as plt import matplotlib.animation as animation import datetime as dt import csv fig = plt.figure() ax1 = fig.add_subplot(1,1,1) def animate(i): # 每次动画更新都创建新的空列表 x = [] y1 = [] y2 = [] y3 = [] with open( 'data_log.csv', 'r') as csvfile: alphasensefile = csv.reader(csvfile, delimiter = ',') # 跳过前三行说明/表头 next(alphasensefile, None) next(alphasensefile, None) next(alphasensefile, None) for column in alphasensefile: a = dt.datetime.strptime((column[0]), '%H:%M:%S') x.append(a) # 注意:CSV读取的是字符串,转成数值类型避免绘图异常 y1.append(float(column[1])) y2.append(float(column[2])) y3.append(float(column[3])) ax1.clear() ax1.plot(x,y1) ani = animation.FuncAnimation(fig, animate, interval=1000) plt.show()
方案2:清空全局列表后再读取数据
如果你需要保留全局变量的结构,可以在每次读取CSV前清空列表:
import matplotlib.pyplot as plt import matplotlib.animation as animation import datetime as dt import csv fig = plt.figure() ax1 = fig.add_subplot(1,1,1) x=[] ; y1=[]; y2=[]; y3=[] def animate(i): # 清空全局列表,避免旧数据残留 x.clear() y1.clear() y2.clear() y3.clear() with open( 'data_log.csv', 'r') as csvfile: alphasensefile = csv.reader(csvfile, delimiter = ',') next(alphasensefile, None) next(alphasensefile, None) next(alphasensefile, None) for column in alphasensefile: a = dt.datetime.strptime((column[0]), '%H:%M:%S') x.append(a) y1.append(float(column[1])) y2.append(float(column[2])) y3.append(float(column[3])) ax1.clear() ax1.plot(x,y1) ani = animation.FuncAnimation(fig, animate, interval=1000) plt.show()
额外优化提示
- 代码里加了
float()转换:CSV读取的所有数据都是字符串类型,直接用于绘图可能会有异常,转成数值类型更稳妥; - 如果CSV文件后期变大,每次读取整个文件会影响性能,可以考虑记录上次读取的行数,下次只读取新增的行(不过对于传感器实时日志,一般数据量不大,当前方案足够用)。
内容的提问来源于stack exchange,提问作者Daniel Zanati
相关产品推荐
相关产品推荐

