Search code examples
pythonmatplotliblivegraph

Real time live graphs in Jupyter Notebook


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)  

Solution

  • 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()