Real time live graphs in Jupyter Notebook

2.4k views Asked by At

I have just started learning python to plot realtime gragh. I have tried solutions provided on stackoverflow but none of them are working. Below is my code and it isn't woorking. Please help

import numpy as np
import matplotlib.pyplot as plt
import pyautogui as pg
from matplotlib.animation import FuncAnimation

%matplotlib notebook

binSize = 512
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
def animate(i):
    try:
        while True:
            x, y = pg.position()
            f.append(x)
    except KeyboardInterrupt:
           print('') 
#     f.append(15) 
    if len(f)<binSize :
        plt.cla()
        plt.plot(f, color='c',LineWidth=1.5,label="Noisy") 
    else:
        plt.cla()
        plt.plot(f[-binSize:],color='c',LineWidth=1.5,label="Noisy")
ani = FuncAnimation(plt.gcf(),animate,interval=1);

So I have updated the code and trying to draw two subplots but after sometime

  1. Upper graph stopped clearing the canvas (Mouse X coordinates)
  2. Lower graph stopped updating the plot (FFT)
  3. When data grows beyond the binSize, notebook freezes and plots update really slowly


    %matplotlib notebook
    
    binSize = 256
    # fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
    f = []
    t = 0
    dt = 1
    fig,axs = plt.subplots(2,1) 
    
    def animate(i):
        x, y = pg.position() 
        f.append(x) 
        n = len(f)
        if n<binSize : 
            plt.sca(axs[0])
            plt.cla()
            plt.plot(f, color='c',LineWidth=1.5,label="MOUSE") 
        else:
            fhat = np.fft.fft(f,binSize)
            PSD = fhat*np.conj(fhat)/binSize
            freq  = (1/(dt*binSize))*np.arange(binSize)
            L = np.arange(1,np.floor(binSize/2),dtype='int')
            
            # update the code third time
      
               
            axs[0].clear() 
            axs[0].plot(f[-binSize:], color='c',LineWidth=1.5,label="MOUSE") 
#           axs[0].xlim(0,binSize) # this stopped the FFT graph to be plotted
        
#           plt.cla()
            axs[1].clear()  
            axs[1].plot(freq[L],PSD[L],color='r',LineWidth=2,label="FFT")  
    #         plt.xlim(t[0],t[-1])
    #         plt.legend()
    
    #         plt.sca(axs[1])
    #         plt.plot(freq[L],PSD[L],color='c',LineWidth=2,label="Mouse FFT") 
    #         plt.xlim(0,300)
    #         plt.legend()
    #         plt.cla()
    #         plt.plot(f[-binSize:],color='c',LineWidth=1.5,label="Mouse")
            
    ani = FuncAnimation(plt.gcf(),animate,interval=dt)  
2

There are 2 answers

6
furas On BEST ANSWER

To make it faster you may reduce data like in other answer

f.pop(0)

I use also different method to update plot which works much faster on my computer.

I create empty plots at start

# needs `,` to get first element from list
p1, = axs[0].plot([], [], color='c', LineWidth=1.5, label="MOUSE")
p2, = axs[1].plot([], [], color='r', LineWidth=2,   label="FFT")

and later only update data in plots without clear() and plot() again

        xdata = range(len(f))
        ydata = f
        p1.set_data(xdata, ydata)

and

        # replace data in plot
        xdata = range(binSize)
        ydata = f[-binSize:]
        p1.set_data(xdata, ydata)
        #p1.set_xdata(xdata)
        #p1.set_ydata(ydata)

        # replace data in plot
        xdata = freq[:(binSize//2)]
        ydata = PSD[:(binSize//2)]
        p2.set_data(xdata, ydata)

It needs only to run code which rescale plot

    # rescale view
    axs[0].relim()
    axs[0].autoscale_view(True,True,True)
    axs[1].relim()
    axs[1].autoscale_view(True,True,True)

animate() has to also return new plots

    # return plots
    return p1, p2

And FuncAnimation() has to blit them

ani = FuncAnimation(..., blit=True)

EDIT:

Animation works much, much faster also because I run it normally python script.py, not in Jupuyter Notebook

EDIT:

when I run normally I found one problem which I could find solution: it doesn't update values/ticks on axes. Jupyter Notebook doesn't have this problem.


import numpy as np
import matplotlib.pyplot as plt
import pyautogui as pg
from matplotlib.animation import FuncAnimation

%matplotlib notebook

binSize = 256

f = []
t = 0
dt = 1
fig, axs = plt.subplots(2, 1) 

# needs `,` to get first element from list
p1, = axs[0].plot([], [], color='c', LineWidth=1.5, label="MOUSE")
p2, = axs[1].plot([], [], color='r', LineWidth=2,   label="FFT")

freq = np.arange(binSize)/(dt*binSize)

def animate(i):
    x, y = pg.position() 
    n = len(f)

    if n < binSize :  
        f.append(x)

        # replace data in plot        
        xdata = range(len(f))
        ydata = f
        p1.set_data(xdata, ydata)
        #p1.set_xdata(xdata)
        #p1.set_ydata(ydata)
    else:
        f.pop(0)
        f.append(x)
        
        fhat = np.fft.fft(f, binSize)
        PSD  = fhat * np.conj(fhat) / binSize
        
        # replace data in plot
        #xdata = range(binSize)
        ydata = f[-binSize:]
        #p1.set_data(xdata, ydata)
        #p1.set_xdata(xdata)
        p1.set_ydata(ydata)

        # replace data in plot
        xdata = freq[:(binSize//2)]
        ydata = PSD[:(binSize//2)]
        p2.set_data(xdata, ydata)

    # rescale view
    axs[0].relim()
    axs[0].autoscale_view(True,True,True)
    axs[1].relim()
    axs[1].autoscale_view(True,True,True)
        
    # return plots
    return p1, p2

ani = FuncAnimation(plt.gcf(), animate, interval=dt, blit=True)

plt.show()
0
B L Λ C K On

You should try this. Instead of clearing the plt clear axs[0] and so on. Also, instead of plotting on plt.plot, plot on axs[0].plot

%matplotlib notebook

binSize = 256
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
t = 0
dt = 1
fig,axs = plt.subplots(2,1) 
plt.sca(axs[0]) 
plt.sca(axs[1])

def animate(i):
    x, y = pg.position() 
    n = len(f)
    if n<binSize :  
        f.append(x*100) 
        axs[0].clear() 
        axs[0].plot(f, color='c',LineWidth=1.5,label="MOUSE") 
    else:
        f.pop(0)
        f.append(x) 
        fhat = np.fft.fft(f,binSize)
        PSD = fhat*np.conj(fhat)/binSize
        freq  = (1/(dt*binSize))*np.arange(binSize)
        L = np.arange(1,np.floor(binSize/2),dtype='int') # index array of  [1,2,3..... binsize/2] type int
        
        axs[0].clear() 
        axs[0].plot(f[-binSize:], color='c',LineWidth=1.5,label="MOUSE")  
        
        axs[1].clear()  
        axs[1].plot(freq[L],PSD[L],color='r',LineWidth=2,label="FFT")   
        
ani = FuncAnimation(plt.gcf(),animate,interval=dt) 
plt.show()