【问题标题】:Correct way to animate big amounts of data on Matplotlib?在 Matplotlib 上为大量数据设置动画的正确方法?
【发布时间】:2018-05-02 16:47:27
【问题描述】:

我正在做一个涉及数十个文件的项目,每个文件都有数百个点,每个文件代表 matplotlib 上的一条线,这些文件将由另一个软件自动更新,我的目标是不断绘制其中,第一种文件格式如下:

"Convergence history of Static Temperature on p14 (in SI units)"
"Iteration" "Vertex Average Static Temperature"
10 358.3162536621094
20 369.484375
30 375.3867797851562
40 378.8209228515625
50 380.5880432128906
60 381.4667663574219
70 381.8531494140625
80 381.9440307617188
...

第一个数字代表"X"轴和第二个"Y",这是".out",很容易解析这些数据,甚至动画。

另一种格式如下的文件包含要在matplotlib上绘制的5行,格式如下:

1 {{continuity 1.0000e+00} {x-velocity 0.0000e+00} {y-velocity 4.3827e-02} {z-velocity 1.9319e-03} {energy 1.2276e-07} }
2 {{continuity 1.0000e+00} {x-velocity 7.8061e-04} {y-velocity 1.6308e+01} {z-velocity 1.0320e-03} {energy 2.9296e-06} }
3 {{continuity 2.3509e-01} {x-velocity 1.2848e-03} {y-velocity 1.2352e-02} {z-velocity 1.0337e-02} {energy 2.2715e-06} }
4 {{continuity 8.0945e-02} {x-velocity 1.6650e-03} {y-velocity 1.3073e-02} {z-velocity 1.0491e-02} {energy 3.2993e-07} }

第一个数字代表"X" 轴,大括号"{}" 之间的每一行/名称都有它的"Y " 值,它们的扩展名是".res",我正在使用正则表达式来解析它们的数据。

预期行为

代码应该读取所​​有文件,按类别排序,然后我会在每个类别上调用 animate 函数,animate 函数将读取该类别上的每个文件,并将其绘制到 matplotlib 图中,每个 x时间。

问题

现在的代码部分工作,因为它确实效率低下,它甚至没有完成读取它需要在动画函数再次执行之前绘制的所有文件,我相信正则表达式在这里是一个大问题,因为它更慢,我需要找到一种方法来仅读取最新点而不是重新绘制所有文件。

  • 我是否应该争取更长的动画间隔时间?
  • 我应该在每个文件而不是每个类别上调用动画吗? (我可能同时运行大约 20-30 个动画函数) 这很糟糕吗?
  • 我应该使用线程来提高性能吗?

  • 我是面临 matplotlib 动画功能的限制,还是只是执行不好?


代码:

查找要绘制的文件的函数。

def find_plot_files(self, file_path, n):
    project_name = file_path.split("/")[-1]  # Erase everything before the last slash:  /ab/bc/tu.out > tu.out
    project_name = project_name[:project_name.find(".")]
    current_project = self.controller.project_list.create_project(project_name, file_path)
    file_path = os.path.dirname(file_path)

    for file in os.listdir(file_path):
        graph_name = file[file.find(".") + 1:file.rfind(".")]
        if file.endswith(".out"):
            if project_name in file:
                current_file = open(file_path + "/" + file, 'r').read()
                if 'Temperature' in current_file:
                    current_project.insert_graph('Temperature', graph_name, file_path+"/"+file)
                elif 'Pressure' in current_file:
                    current_project.insert_graph('Pressure', graph_name, file_path+"/"+file)
                elif 'Velocity' in current_file:
                    current_project.insert_graph('Velocity', graph_name, file_path+"/"+file)
        elif file.endswith(".res"):
            if project_name in file:
                current_project.insert_graph('Residuals', graph_name, file_path+"/"+file)

#Here every "graph" represents a category, and i will create a figure and plot every line into it.

    for graph in self.controller.project_list.get_project(project_name).get_graphs():
        f = Figure(figsize=(5, 5), dpi=100)
        a = f.add_subplot(111)
        canvas = FigureCanvasTkAgg(f, self)
        f.suptitle(graph)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        toolbar = NavigationToolbar2Tk(canvas, self)
        toolbar.update()
        canvas._tkcanvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.animated_graph.append(animation.FuncAnimation(f, self.animate, fargs=(a, project_name, graph),
                                                           interval=3000))

动画功能

def animate(self, i, target_graph, project_name, graph):
    target_graph.clear()
    for graph in self.controller.project_list.get_project(project_name).get_graphs()[graph]:
        file_path = graph.file_path
        pull_data = open(file_path, 'r').read()
        data_array = pull_data.split('\n')
        xar = []
        # THIS PLOTS THE SECOND CASE THAT I COPIED IN THE QUESTION
        if graph.file_path[-3:] == 'res':
            lines_list = {}
            for each_line in data_array:
                if len(each_line) > 0:
                    xar.append(each_line[0:each_line.find(" ")])
                    each_line = each_line[each_line.find(" ")+2: -1]
                    for data_point in re.findall("\{(.*?)\}", each_line):
                        data_point = data_point.split()
                        if data_point[0] in lines_list:
                            lines_list[data_point[0]].append(data_point[1])
                        else:
                            lines_list[data_point[0]] = [data_point[1]]
            for key in lines_list:
                target_graph.plot(xar, lines_list[key], label=key)
        else:
            # THIS PLOTS THE FIRST CASE THAT I COPIED IN THE QUESTION
            yar = []
            for each_line in data_array:
                try:
                    if len(each_line.split()) == 2:
                        x, y = each_line.split()
                        xar.append(float(x))
                        yar.append(float(y))
                except TypeError:
                    pass
            target_graph.plot(xar, yar)

我真的是使用 matplotlib 和 animate 函数的新手,所以非常感谢任何帮助!

提前致谢!

【问题讨论】:

  • 请投反对票的 cmets。

标签: python python-3.x python-2.7 matplotlib


【解决方案1】:

由于你问了几个问题,而且我不是动画专家,所以请让我先提示一下阅读数据:

也许依赖可用 python 包的导入器函数更快,这些包针对诸如 pandas 之类的任务进行了优化。

import pandas as pd

这对于您的“.out”文件来说非常简单:

In:pd.read_csv(filename, skiprows=1, sep=' ')
Out: 
   Iteration  Vertex Average Static Temperature
0         10                         358.316254
1         20                         369.484375
2         30                         375.386780
3         40                         378.820923
4         50                         380.588043
5         60                         381.466766
6         70                         381.853149
7         80                         381.944031

“.res”文件看起来更复杂,但您可以先看看它们:

pd.read_table(filename, names=None, sep='[{}\s]+', engine='python')

   1  continuity  1.0000e+00  x-velocity  0.0000e+00  y-velocity  4.3827e-02  \
0  2  continuity    1.000000  x-velocity    0.000781  y-velocity   16.308000   
1  3  continuity    0.235090  x-velocity    0.001285  y-velocity    0.012352   
2  4  continuity    0.080945  x-velocity    0.001665  y-velocity    0.013073   

   z-velocity  1.9319e-03  energy    1.2276e-07  Unnamed: 11  
0  z-velocity    0.001032  energy  2.929600e-06          NaN  
1  z-velocity    0.010337  energy  2.271500e-06          NaN  
2  z-velocity    0.010491  energy  3.299300e-07          NaN  

从此结果中,您可以检索您感兴趣的列号,并将此信息用于将来的导入:

pd.read_table(filename, usecols=[2,4,6,8,10], names=['x', 'x-vel', 'y-vel', 'z-vel', 'energy'], sep='[{}\s]+', engine='python')

          x     x-vel      y-vel     z-vel        energy
0  1.000000  0.000000   0.043827  0.001932  1.227600e-07
1  1.000000  0.000781  16.308000  0.001032  2.929600e-06
2  0.235090  0.001285   0.012352  0.010337  2.271500e-06
3  0.080945  0.001665   0.013073  0.010491  3.299300e-07

结果是 pandas 数据帧,它提供了一组非常大的函数以供进一步处理,包括如果您愿意,只需访问它们的值的 np.array 表示。

也许这有点帮助。

【讨论】:

  • 这是一个很棒的提示,我会尝试并提出意见。
  • 当点数过高时,colspecs 出现了一些问题,它向右移动,并且数据开始以错误的格式出现“29 5.3849e-0 1.3535e- 0 1.0677e-0 4.2473e-0 6.3448e-0 .. ... ... ... ... ... 970 y 6.2748e- y 3.1410e- y 1.4403e- y 8.5215e- y 1.7764e -”,有没有更好的方法来做到这一点?
  • 试试pd.read_table(filename, names=None, sep='[{}\s]+', engine='python') 抱歉,现在没时间了...之后添加名称...
  • 查看结果并记下您需要的列数。然后代替names=None 使用usecols=_list_of_numbers_names=_list_oft_names
猜你喜欢
  • 1970-01-01
  • 2011-05-07
  • 2018-08-10
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-22
  • 1970-01-01
相关资源
最近更新 更多