Правильный способ анимировать большие объемы данных на Matplotlib? - PullRequest
0 голосов
/ 02 мая 2018

Я работаю над проектом, который включает в себя десятки файлов, каждый файл имеет сотни точек, и каждый файл представляет собой строку в 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", их легко анализировать и даже анимировать.

Файл другого типа со следующим форматом содержит 5 строк для построения на matplotlib, который имеет следующий формат:

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 количество времени.

Проблема

Код сейчас работает частично, потому что он действительно неэффективен, он даже не завершает чтение всех файлов, которые нужно построить, до того, как функция animate хочет выполнить снова, я считаю, что регулярное выражение это большая проблема, так как он медленнее и мне нужно найти способ прочитать только самую новую точку, а не перепланировать все файлы.

  • Должен ли я стремиться увеличить время между анимациями?
  • Должен ли я вызывать animate для каждого файла вместо каждой категории? (Возможно, у меня одновременно работает около 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, поэтому любая помощь будет очень полезна!

Заранее спасибо!

1 Ответ

0 голосов
/ 03 мая 2018

Поскольку вы задаете несколько вопросов, а я не эксперт по анимации, пожалуйста, позвольте мне дать первую подсказку для чтения в данных:

Возможно, быстрее полагаться на функции импорта доступных пакетов python, которые оптимизированы для таких задач, как задачи панд.

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-представлению их значений.

Возможно, это немного помогает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...