web-dev-qa-db-fra.com

Tracez un graphique changeant dynamiquement à l'aide de matplotlib dans Jupyter Notebook

J'ai un tableau 2D M x N: la rangée représente cette valeur de N points à l'instant i.

Je veux visualiser les points [1 ligne du tableau] sous la forme d'un graphique où les valeurs sont mises à jour après un petit intervalle. Ainsi, le graphique affiche 1 ligne à la fois, puis met à jour les valeurs sur la ligne suivante, etc.

Je veux faire cela dans un cahier Jupyter. Vous recherchez des codes de référence.

J'ai essayé de suivre les choses mais sans succès:

23
Anuj Gupta
6
Anuj Gupta

Voici une solution alternative, peut-être plus simple:

%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt

m = 100
n = 100
matrix = np.random.normal(0,1,m*n).reshape(m,n)

fig = plt.figure()
ax = fig.add_subplot(111)
plt.ion()

fig.show()
fig.canvas.draw()

for i in range(0,100):
    ax.clear()
    ax.plot(matrix[i,:])
    fig.canvas.draw()
44
Graham S

J'étais particulièrement à la recherche d'une bonne solution pour le scénario dans lequel un thread pompe des données et nous souhaitons que le bloc-notes Jupyter continue à mettre à jour le graphique sans rien bloquer. Après avoir examiné environ une douzaine de réponses connexes, voici quelques conclusions:

Attention

N'utilisez pas la magie ci-dessous si vous voulez un graphe en direct. La mise à jour du graphique ne fonctionne pas si le bloc-notes utilise ci-dessous:

%load_ext autoreload
%autoreload 2

Avant d’importer matplotlib, vous avez besoin de magie dans votre cahier:

%matplotlib notebook

Méthode 1: Utiliser FuncAnimation

La mise à jour du graphique a toutefois un inconvénient, même si vos données n'ont pas encore été mises à jour. L'exemple ci-dessous montre un autre thread mettant à jour les données alors que le cahier Jupyter met à jour le graphique via FuncAnimation.

%matplotlib notebook

from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from random import randrange
from threading import Thread
import time

class LiveGraph:
    def __init__(self):
        self.x_data, self.y_data = [], []
        self.figure = plt.figure()
        self.line, = plt.plot(self.x_data, self.y_data)
        self.animation = FuncAnimation(self.figure, self.update, interval=1000)
        self.th = Thread(target=self.thread_f, daemon=True)
        self.th.start()

    def update(self, frame):
        self.line.set_data(self.x_data, self.y_data)
        self.figure.gca().relim()
        self.figure.gca().autoscale_view()
        return self.line,

    def show(self):
        plt.show()

    def thread_f(self):
        x = 0
        while True:
            self.x_data.append(x)
            x += 1
            self.y_data.append(randrange(0, 100))   
            time.sleep(1)  

g = LiveGraph()
g.show()

Méthode 2: mise à jour directe

La deuxième méthode consiste à mettre à jour le graphique au fur et à mesure que les données arrivent de un autre thread . Ceci est risqué car matplotlib n’est pas thread-safe mais cela semble fonctionner tant qu’il n’ya qu’un seul thread effectuant des mises à jour.

%matplotlib notebook

from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from random import randrange
from threading import Thread
import time

class LiveGraph:
    def __init__(self):
        self.x_data, self.y_data = [], []
        self.figure = plt.figure()
        self.line, = plt.plot(self.x_data, self.y_data)

        self.th = Thread(target=self.thread_f, daemon=True)
        self.th.start()

    def update_graph(self):
        self.line.set_data(self.x_data, self.y_data)
        self.figure.gca().relim()
        self.figure.gca().autoscale_view()

    def show(self):
        plt.show()

    def thread_f(self):
        x = 0
        while True:
            self.x_data.append(x)
            x += 1
            self.y_data.append(randrange(0, 100))  

            self.update_graph()

            time.sleep(1)  


from live_graph import LiveGraph

g = LiveGraph()
g.show()
3
Shital Shah

J'ai exploré ceci et produit ce qui suit est en grande partie auto-documenté:

import matplotlib.pyplot as plt
%matplotlib notebook

print('This text appears above the figures')
fig1 = plt.figure(num='DORMANT')
print('This text appears betweeen the figures')
fig2 = plt.figure()
print('This text appears below the figures')

fig1.canvas.set_window_title('Canvas active title')
fig1.suptitle('Figure title', fontsize=20)

# Create plots inside the figures
ax1 = fig1.add_subplot(111)
ax1.set_xlabel('x label')
ax2 = fig2.add_subplot(111)

# Loop to update figures
end = 40
for i in range(end):
    ax2.cla()  # Clear only 2nd figure's axes, figure 1 is ADDITIVE
    ax1.set_title('Axes title')  # Reset as removed by cla()

    ax1.plot(range(i,end), (i,)*(end-i))
    ax2.plot(range(i,end), range(i,end), 'rx')
    fig1.canvas.draw()
    fig2.canvas.draw()
1
Tom Hale

En plus de @ 0aslam0, j'ai utilisé le code de ici . Je viens de changer la fonction animate pour obtenir la ligne suivante à chaque fois. Il dessine une évolution animée (M étapes) de tous les N points.

from IPython.display import HTML
import numpy as np
from matplotlib import animation
N = 5
M = 100
points_evo_array = np.random.Rand(M,N)

# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, M), ylim=(0, np.max(points_evo_array)))
lines = []

lines = [ax.plot([], [])[0] for _ in range(N)]

def init():    
    for line in lines:
        line.set_data([], [])
    return lines

def animate(i):
    for j,line in enumerate(lines):
        line.set_data(range(i), [points_evo_array[:i,j]])
    return lines

# call the animator.  blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate,np.arange(1, M), init_func=init, interval=10, blit=True)

HTML(anim.to_html5_video())

J'espère que ça vous sera utile

1
segevara

Voici une bibliothèque qui traite les données de traçage/enregistrement en temps réel ( joystick ), bien que je ne sois pas sûr que cela fonctionne avec jupyter. Vous pouvez l'installer en utilisant l'habituel pip install joystick.

Difficile de faire une solution de travail sans plus de détails sur vos données. Voici une option:

import joystick as jk
import numpy as np

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 docs
        """
        # INIT DATA HERE
        self.shape = (10, 4) # M, N
        self.data = np.random.random(self.shape)
        self.xaxis = range(self.shape[1])
        ############
        # create a graph frame
        self.mygraph = self.add_frame(
                   jk.Graph(name="TheName", size=(500, 500), pos=(50, 50),
                            fmt="go-", xnpts=self.shape[1], freq_up=5, bgcol="w",
                            xylim=(0, self.shape[1]-1, None, None)))

    @_infinite_loop(wait_time=0.5)
    def _generate_fake_data(self):  # function looped every 0.5 second
        """
        Loop starting with the simulation start, getting data and
        pushing it to the graph every 0.5 seconds
        """
        # NEW (RANDOM) DATA
        new_data = np.random.random(self.shape[1])
        # concatenate data
        self.data = np.vstack((self.data, new_data))
        # Push new data to the graph
        self.mygraph.set_xydata(self.xaxis, self.data[-1])

t = test()
t.start()

t.stop()
t.exit()

Ce code créera un graphique à mise à jour automatique 5 fois par seconde (freq_up = 5), tandis que les nouvelles données sont générées (de manière aléatoire) toutes les 0,5 seconde (wait_time = 0.5) et poussées vers le graphique pour affichage.

Si vous ne voulez pas que l'axe des Y bouge, tapez t.mygraph.xylim = (0, t.shape[1]-1, 0, 1).

0
Guillaume S