Динамически обновляемый сюжет в matplotlib
Я делаю приложение на Python, которое собирает данные из последовательного порта и строит график собранных данных в зависимости от времени прибытия. Время прибытия данных неизвестно. Я хочу, чтобы график обновлялся при получении данных. Я искал, как это сделать, и нашел два метода:
- Очистите график и заново нарисуйте график со всеми точками снова.
- Анимируйте сюжет, изменив его через определенный интервал.
Я не предпочитаю первый, поскольку программа работает и собирает данные в течение длительного времени (например, день), и перерисовка графика будет довольно медленной. Второй вариант также не является предпочтительным, так как время поступления данных является неопределенным, и я хочу, чтобы график обновлялся только при получении данных.
Есть ли способ, которым я могу обновить график, просто добавив больше точек к нему только при получении данных?
3 ответа
Есть ли способ, которым я могу обновить график, просто добавив больше точек [s] к нему...
Существует несколько способов анимации данных в matplotlib, в зависимости от имеющейся версии. Вы видели примеры поваренной книги matplotlib? Также ознакомьтесь с более современными примерами анимации в документации по 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
,
Чтобы сделать это без 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()
Вот способ, который позволяет удалять точки после нанесения определенного количества точек:
import matplotlib.pyplot as plt
# generate axes object
ax = plt.axes()
# set limits
plt.xlim(0,10)
plt.ylim(0,10)
for i in range(10):
# add something to axes
ax.scatter([i], [i])
ax.plot([i], [i+1], 'rx')
# draw the plot
plt.draw()
plt.pause(0.01) #is necessary for the plot to update for some reason
# start removing points if you don't want all shown
if i>2:
ax.lines[0].remove()
ax.collections[0].remove()
Я знаю, что опоздал, чтобы ответить на этот вопрос, но по вашей проблеме вы можете посмотреть в пакет "джойстик". Я разработал его для вывода потока данных из последовательного порта, но он работает для любого потока. Он также позволяет вести интерактивную запись текста или изображения (в дополнение к графику). Не нужно создавать свои собственные циклы в отдельном потоке, об этом позаботится пакет, просто укажите желаемую частоту обновления. Плюс терминал остается доступным для мониторинга команд во время построения графика. См. 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()