Динамически обновляемый сюжет в 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()