Je réalise une application dans Python qui collecte des données à partir d'un port série et trace un graphique des données collectées en fonction du temps d'arrivée. L'heure d'arrivée des données est incertaine. Je veux que l'intrigue soit mise à jour lorsque les données sont reçues. J'ai cherché comment faire cela et trouvé deux méthodes:
Je ne préfère pas le premier car le programme exécute et collecte des données sur une longue période (un jour par exemple), et le tracé de l’intrigue sera plutôt lent. La seconde n'est également pas préférable car l'heure d'arrivée des données est incertaine et je souhaite que le graphique ne soit mis à jour que lorsque les données sont reçues.
Existe-t-il un moyen de mettre à jour l'intrigue en ajoutant simplement plus de points à la réception des données?
Existe-t-il un moyen de mettre à jour l'intrigue en y ajoutant simplement un point [s] ...
Il existe différentes manières d’animer des données dans matplotlib, selon la version dont vous disposez. Avez-vous vu les exemples livre de recettes matplotlib ? Découvrez également le plus moderne exemples d'animation dans la documentation de matplotlib. Enfin, API d'animation définit une fonction FuncAnimation qui anime une fonction dans le temps. Cette fonction pourrait simplement être la fonction que vous utilisez pour acquérir vos données.
Chaque méthode définit en gros la propriété data
de l'objet en cours de dessin, il n'est donc pas nécessaire d'effacer l'écran ou la figure. La propriété data
peut simplement être étendue pour que vous puissiez conserver les points précédents et continuer à ajouter des éléments à votre ligne (ou à votre image ou à ce que vous dessinez).
Étant donné que vous dites que l'heure d'arrivée des données est incertaine, votre meilleur choix est probablement de faire quelque chose comme:
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()
Ensuite, lorsque vous recevez des données du port série, appelez simplement update_line
.
Afin de faire cela sans FuncAnimation (par exemple, vous voulez exécuter d'autres parties du code pendant que le tracé est produit ou vous voulez mettre à jour plusieurs parcelles simultanément), l'appel de draw
seule ne produit pas la parcelle (au moins avec le backend qt).
Ce qui suit fonctionne pour moi:
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()
Je sais que je suis en retard pour répondre à cette question, mais pour votre problème, vous pourriez vous pencher sur le package "joystick". Je l'ai conçu pour tracer un flux de données à partir du port série, mais cela fonctionne pour n'importe quel flux. Il permet également la journalisation interactive du texte ou le traçage des images (en plus du traçage des graphes). Pas besoin de faire vos propres boucles dans un thread séparé, le paquet s'en occupe, donnez simplement la fréquence de mise à jour que vous souhaitez. De plus, le terminal reste disponible pour surveiller les commandes pendant le traçage. Voir http://www.github.com/ceyzeriat/joystick/ ou https://pypi.python.org/pypi/joystick (utilisez le joystick d'installation de pip pour installer )
Il suffit de remplacer np.random.random () par votre point de données réel lu depuis le port série dans le code ci-dessous:
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()
Voici un moyen qui permet de supprimer des points après un certain nombre de points tracés:
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()