Живые графики в реальном времени в Jupyter Notebook

Я только начал изучать Python для построения графиков в реальном времени. Я пробовал решения, представленные в stackru, но ни одно из них не работает. Ниже мой код, и он не работает. Пожалуйста помоги

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);

Итак, я обновил код и попытался нарисовать два подзаговора, но через некоторое время

  1. Верхний график перестал очищать холст (координаты X мыши)
  2. Нижний график перестал обновлять график (БПФ)
  3. Когда объем данных превышает размер binSize, ноутбук зависает и графики обновляются очень медленно


    %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 ответа

Решение

Чтобы сделать это быстрее, вы можете уменьшить данные, как в другом ответе

f.pop(0)

Я также использую другой метод обновления сюжета, который на моем компьютере работает намного быстрее.

Я создаю пустые участки на старте

# 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")

а позже обновлять данные только на графиках без clear() а также plot() еще раз

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

а также

        # 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)

animate() также должен вернуть новые участки

    # return plots
    return p1, p2

А также FuncAnimation() должен убить их

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

РЕДАКТИРОВАТЬ:

Анимация работает намного, намного быстрее, потому что я запускаю ее как обычно python script.py, не в Jupuyter Notebook

РЕДАКТИРОВАТЬ:

когда я бегу нормально, я обнаружил одну проблему, которую смог найти решение: он не обновляет значения / отметки на осях. Jupyter Notebook нет этой проблемы.


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

Вы должны попробовать это. Вместо очистки plt clear axs[0] и так далее. Кроме того, вместо построения на plt.plot, строите на 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()
Другие вопросы по тегам