Python 使用matplotlib设置多个轴的动画,仅在新数据到达时更新图形

Python 使用matplotlib设置多个轴的动画,仅在新数据到达时更新图形,python,matplotlib,Python,Matplotlib,我将数据存储在JSON格式的stock_数据中(可以是任意数据)。我想绘制4个轴,当有新数据时,更新图形(我假设通过动画) 我只希望在使用日内数据时发生这种情况(正如您可以看到的,我在底部有一个if日内检查)。我从API中提取这些日内数据。此API大约每分钟更新一次,并且仅在特定时间内更新。我不介意它不立即更新,但最好在新数据发布后1分钟内更新 我尝试过提取新数据并将其与旧的DF进行比较(如代码末尾所示),然后将其放入while-True:loop,但是图形无法呈现。我尝试过简单地将整个函数放在

我将数据存储在JSON格式的stock_数据中(可以是任意数据)。我想绘制4个轴,当有新数据时,更新图形(我假设通过动画)

我只希望在使用日内数据时发生这种情况(正如您可以看到的,我在底部有一个if日内检查)。我从API中提取这些日内数据。此API大约每分钟更新一次,并且仅在特定时间内更新。我不介意它不立即更新,但最好在新数据发布后1分钟内更新

我尝试过提取新数据并将其与旧的DF进行比较(如代码末尾所示),然后将其放入while-True:loop,但是图形无法呈现。我尝试过简单地将整个函数放在一个循环中,每次都渲染图形-这不仅需要花费很长时间来渲染,而且如果放大图形,它会完全重置它。我想这是重绘的问题

最后,我不确定在动画中放置什么。FuncAnimation()也不确定。。我排除了ax3和ax4,因为出于演示目的,它们的行为与ax2相同。非常感谢你的帮助

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
import matplotlib.animation as animation
from mpl_finance import candlestick_ohlc
import numpy as np

## CANDLESTICK GRAPH ## 
def candlestick(symbol, MA1 = 20, MA2 = 200):
    try:
        ## arbitrary colors ##
        candle_upcol = '#cccccc'
        candle_downcol = '#cccccc'
        fill_col = '#cccccc'
        bg_col = '#cccccc'
        spine_col = '#cccccc'
        ## load stocks ##
        stock_data = pd.DataFrame.from_dict(json.load(open('db/AAPL.txt')), orient = 'index', dtype = np.float64)
        stock_data = stock_data.values
        ## BEGIN PLOTTING ##
        start_point = len(stock_data[max(MA1, MA2)-1:])
        fig = plt.figure(facecolor=bg_col)
        #set grids
        ax1 = plt.subplot2grid((8,4), (1,0), rowspan = 5, colspan = 4, facecolor = bg_col)
        ax2 = plt.subplot2grid((8,4), (7,0), rowspan = 1, colspan = 4, sharex = ax1, facecolor= bg_col)
        ax3 = plt.subplot2grid((8,4), (0,0), rowspan = 1, colspan = 4, sharex = ax1, facecolor = bg_col)
        ax4 = plt.subplot2grid((8,4), (6,0), rowspan = 1, colspan = 4, sharex = ax1, facecolor = bg_col)

        #PRICE plot (AX1)
        candlestick_ohlc(ax1, stock_data[-start_point:,0:5], width = 0.6, colorup = candle_upcol, colordown = candle_downcol)
        ax1.xaxis.set_major_locator(mticker.MaxNLocator(10))
        ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
        ax1.grid(True)
        plt.setp(ax1.get_xticklabels(), visible=False) #remove x ticks

        #MOVING AVERAGES plot (AX1)
        if MA1 != 0:
            av1 = moving_average(stock_data[:,4], MA1) #using close prices
            label_ma1 = '{MA} SMA'.format(MA = str(MA1))
            ax1.plot(stock_data[-start_point:,0], av1[-start_point:], label = label_ma1, color = '#aec6cf', linewidth = .8)
        if MA2 != 0:
            av2 = moving_average(stock_data[:,4], MA2) #using close prices
            label_ma2 = '{MA} SMA'.format(MA = str(MA2))
            ax1.plot(stock_data[-start_point:,0], av2[-start_point:], label = label_ma2, color = '#ffb347', linewidth = .8)
        if MA1 != 0 or MA2 != 0:
            ax1.text(0, 1, 'MA ({MA1}, {MA2})'.format(MA1 = str(MA1), MA2 = str(MA2)), va = 'top', ha = 'left', color = 'w', transform = ax1.transAxes, alpha = 0.5, fontweight = 'bold')

        #VOLUME plot (AX2)
        volume_min = 0 #stock_data[:,5].min()
        ax2.plot(stock_data[-start_point:,0], stock_data[-start_point:,5], '#00ffe8', linewidth = .8)
        ax2.fill_between(stock_data[-start_point:,0], volume_min, stock_data[-start_point:,5], facecolor = fill_col, alpha = 0.5)
        ax2.axes.yaxis.set_ticklabels([]) #remove y ticks
        ax2.text(0, 1, 'VOLUME', va = 'top', ha = 'left', color = 'w', transform = ax2.transAxes, alpha = 0.5, fontweight = 'bold')

        #RSI plot (AX3)
        #similar to VOL

        #MACD plot (AX4)
        #similar to VOL

        #SHARED plot (ALL AX)
        for all_ax in (ax1, ax2''', ax3, ax4'''):
            plt.setp(all_ax.spines.values(), color=spine_col)
            all_ax.tick_params(axis='both', colors = 'w')
            all_ax.yaxis.label.set_color("w")
            all_ax.yaxis.tick_right()
            all_ax.xaxis.set_tick_params(labelsize=9)
            all_ax.yaxis.set_tick_params(labelsize=9)

        #ENTIRE plot
        plt.subplots_adjust(hspace = 0)
        fig.autofmt_xdate()
        fig.suptitle('{STOCK}'.format(STOCK = symbol), color = 'w', fontweight='bold', alpha = 0.75)
        print('Drawing graph.')
        if data_type != 'Intraday':
            print('Graphing complete.')
        else:
            #this will be replaced by an API fetch function at some point, this is just for testing if animation works.. needs a sleep function? and while True loop..?
            new_stock_data = pd.DataFrame.from_dict(json.load(open('db/AAPL_new.txt')), orient = 'index', dtype = np.float64)
            new_stock_data = new_stock_data.values

            if (new_stock_data[-1] == stock_data[-1]).all() == False:
                stock_data = np.vstack([stock_data, new_stock_data[-1]])

            #ani = animation.FuncAnimation(fig, '''???''', interval = 10000) #blit=True?
        plt.show()
    except:
        print('Failed main loop.')

FuncAnimation
将以
间隔所给定的速率重复绘制(或blit)。如果不需要,可以使用计时器。计时器调用一个函数,该函数将根据某些条件不执行任何操作,或使用新数据更新绘图。这样,您可以确保仅在新数据可用时绘制画布(即条件为真)


这非常有用!你知道如何更新mpl_finance的烛台吗?假设每个烛台都是它自己的“线”(根据我收集的),我不能这样做,例如:ln1,=烛台ohlc(ax1,stock_data[-start_point:,0:5],width=0.6),也不能在我说“附加”时附加它:我将附加stock_数据,然后为这些线设置_数据。。然而,对于条形图和烛台,我不知所措!从mpl_finance更新烛台真的很难。人们可能更愿意清除轴并重新绘制它。有道理,我将通过清除它来更新烛台轴。那么条形图呢?另外,虽然逻辑是合理的,但我不太确定这是否完全回答了我的问题。您只需调用if np.random.rand()>0.9,而我正在检查if(new_stock_data[-1]==stock_data[-1])。all()==False,并需要将新数据附加到我的数据帧以停止无限拉新数据循环。在函数内调用stock_data=np.vstack([stock_data,new_stock_data[-1]])不会在函数外更新我的数据帧。。!
import datetime
import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
t = []
x = []
line, = ax.plot_date(t,x, ls="-")

def update():
    now = datetime.datetime.now()
    if np.random.rand() > 0.9:
        t.append(now)
        x.append(np.random.randn())
        line.set_data(t,x)
        ax.relim()
        ax.autoscale_view()
        fig.canvas.draw_idle()
        message = "new data drawn"
    else:
        message = "no new data"
    print(now.time(), message)


timer = fig.canvas.new_timer(interval=200)
timer.add_callback(update)
timer.start()

plt.show()