Динамически обновляемый сюжет в matplotlib

Я делаю приложение на Python, которое собирает данные из последовательного порта и отображает график собранных данных по времени прибытия. Время прибытия для данных не определено. Я хочу, чтобы сюжет обновлялся при получении данных. Я искал, как это сделать, и нашел два метода:

  • Очистите график и снова нарисуйте график со всеми точками.
  • Анимируйте график, изменив его после определенного интервала.

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

Есть ли способ, по которому я могу обновить график, просто добавив к нему больше очков, только когда данные будут получены?

Ответ 1

Есть ли способ, в котором я могу обновить график, просто добавив к нему больше точек [s]...

Существует несколько способов анимации данных в matplotlib, в зависимости от версии, которую вы имеете. Вы видели примеры matplotlib cookbook? Кроме того, ознакомьтесь с более современными примерами анимации в документации matplotlib. Наконец, API-интерфейс анимации определяет функцию FuncAnimation, которая анимирует функцию во времени. Эта функция может быть просто функцией, которую вы используете для получения ваших данных.

Каждый метод в основном устанавливает свойство data объекта, который рисуется, поэтому не требует очистки экрана или рисунка. Свойство data можно просто расширить, чтобы вы могли сохранить предыдущие точки и просто добавлять в свою линию (или изображение или то, что вы рисуете).

Учитывая, что вы говорите, что время прибытия ваших данных неизвестно, ваш лучший выбор, вероятно, просто для того, чтобы сделать что-то вроде:

import matplotlib.pyplot as plt
import numpy

hl, = plt.plot([], [])

def update_line(hl, new_data):
    hl.set_xdata(numpy.append(hl.get_xdata(), new_data))
    hl.set_ydata(numpy.append(hl.get_ydata(), new_data))
    plt.draw()

Затем, когда вы получаете данные из последовательного порта, просто вызовите update_line.

Ответ 2

Чтобы сделать это без FuncAnimation (например, вы хотите выполнить другие части кода во время создания графика или хотите обновить несколько графиков одновременно), вызов только draw не создает сюжет (по крайней мере, с помощью qt-бэкэнд).

Для меня работает следующее:

import matplotlib.pyplot as plt
plt.ion()
class DynamicUpdate():
    #Suppose we know the x range
    min_x = 0
    max_x = 10

    def on_launch(self):
        #Set up plot
        self.figure, self.ax = plt.subplots()
        self.lines, = self.ax.plot([],[], 'o')
        #Autoscale on unknown axis and known lims on the other
        self.ax.set_autoscaley_on(True)
        self.ax.set_xlim(self.min_x, self.max_x)
        #Other stuff
        self.ax.grid()
        ...

    def on_running(self, xdata, ydata):
        #Update data (with the new _and_ the old points)
        self.lines.set_xdata(xdata)
        self.lines.set_ydata(ydata)
        #Need both of these in order to rescale
        self.ax.relim()
        self.ax.autoscale_view()
        #We need to draw *and* flush
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()

    #Example
    def __call__(self):
        import numpy as np
        import time
        self.on_launch()
        xdata = []
        ydata = []
        for x in np.arange(0,10,0.5):
            xdata.append(x)
            ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2))
            self.on_running(xdata, ydata)
            time.sleep(1)
        return xdata, ydata

d = DynamicUpdate()
d()

Ответ 3

Я знаю, что опаздываю, чтобы ответить на этот вопрос, но для вашей проблемы вы можете посмотреть в пакет "джойстик". Я разработал его для построения потока данных из последовательного порта, но он работает для любого потока. Он также позволяет осуществлять интерактивное ведение журнала или построение изображений (в дополнение к построению графика). Не нужно делать свои собственные петли в отдельном потоке, пакет позаботится об этом, просто дайте частоту обновления, которую вы хотите. Кроме того, терминал остается доступным для контроля команд при построении графика. См. http://www.github.com/ceyzeriat/joystick/ или https://pypi.python.org/pypi/joystick (используйте команду pip install джойстик для установки)

Просто замените np.random.random() вашей реальной точкой данных, прочитанной из последовательного порта, в коде ниже:

import joystick as jk
import numpy as np
import time

class test(jk.Joystick):
    # initialize the infinite loop decorator
    _infinite_loop = jk.deco_infinite_loop()

    def _init(self, *args, **kwargs):
        """
        Function called at initialization, see the doc
        """
        self._t0 = time.time()  # initialize time
        self.xdata = np.array([self._t0])  # time x-axis
        self.ydata = np.array([0.0])  # fake data y-axis
        # create a graph frame
        self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1)))

    @_infinite_loop(wait_time=0.2)
    def _generate_data(self):  # function looped every 0.2 second to read or produce data
        """
        Loop starting with the simulation start, getting data and
    pushing it to the graph every 0.2 seconds
        """
        # concatenate data on the time x-axis
        self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
        # concatenate data on the fake data y-axis
        self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
        self.mygraph.set_xydata(t, self.ydata)

t = test()
t.start()
t.stop()