【问题标题】:How to do dynamic matplotlib plotting with a fixed pandas dataframe?如何使用固定的熊猫数据框进行动态 matplotlib 绘图?
【发布时间】:2020-06-21 18:25:33
【问题描述】:

我有一个名为 benchmark_returnsstrategy_returns 的数据框。两者具有相同的时间跨度。我想找到一种以漂亮的动画样式绘制数据点的方法,以便它显示逐渐加载的所有点。我知道有一个matplotlib.animation.FuncAnimation(),但这通常仅用于实时更新 csv 文件等,但就我而言,我知道我想要使用的所有数据。

我也尝试过使用粗略的plt.pause(0.01) 方法,但是随着点数的绘制,这会大大减慢速度。

这是我目前的代码

x = benchmark_returns.index
y = benchmark_returns['Crypto 30'] 
y2 = benchmark_returns['Dow Jones 30']
y3 = benchmark_returns['NASDAQ'] 
y4 = benchmark_returns['S&P 500']


fig, ax = plt.subplots()
line, = ax.plot(x, y, color='k')
line2, = ax.plot(x, y2, color = 'b')
line3, = ax.plot(x, y3, color = 'r')
line4, = ax.plot(x, y4, color = 'g')

def update(num, x, y, y2, y3, y4, line): 
    line.set_data(x[:num], y[:num])
    line2.set_data(x[:num], y2[:num])
    line3.set_data(x[:num], y3[:num])
    line4.set_data(x[:num], y4[:num])

    return line, line2, line3, line4,

ani = animation.FuncAnimation(fig, update, fargs=[x, y, y2, y3, y4, line], 
                              interval = 1, blit = True)
plt.show()

【问题讨论】:

    标签: python pandas matplotlib animation


    【解决方案1】:

    你可以试试matplotlib.animation.ArtistAnimation。它的操作类似于FuncAnimation,因为您可以指定帧间隔、循环行为等,但所有绘图都是在动画步骤之前立即完成的。这是一个例子

    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    from matplotlib.animation import ArtistAnimation
    
    n = 150
    x = np.linspace(0, np.pi*4, n)
    df = pd.DataFrame({'cos(x)' : np.cos(x), 
                       'sin(x)' : np.sin(x),
                       'tan(x)' : np.tan(x),
                       'sin(cos(x))' : np.sin(np.cos(x))})
    
    fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(10,10))
    lines = []
    artists = [[]]
    for ax, col in zip(axs.flatten(), df.columns.values):
        lines.append(ax.plot(df[col])[0])
        artists.append(lines.copy())
    
    anim = ArtistAnimation(fig, artists, interval=500, repeat_delay=1000)
    

    这里的缺点是每个艺术家要么被绘制,要么不被绘制,即你不能只绘制 Line2D 对象的一部分而不进行剪切。如果这与您的用例不兼容,那么您可以尝试使用 FuncAnimationblit=True 并分块每次要绘制的数据以及使用 set_data() 而不是在每次迭代时清除和重绘。使用上述相同数据的示例:

    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    from matplotlib.animation import FuncAnimation
    
    n = 500
    nf = 100
    x = np.linspace(0, np.pi*4, n)
    df = pd.DataFrame({'cos(x)' : np.cos(x), 
                       'sin(x)' : np.sin(x),
                       'tan(x)' : np.tan(x),
                       'sin(cos(x))' : np.sin(np.cos(x))})
    
    fig, axs = plt.subplots(2, 2, figsize=(5,5), dpi=50)
    lines = []
    for ax, col in zip(axs.flatten(), df.columns):
        lines.append(ax.plot([], lw=0.5)[0])
        ax.set_xlim(x[0] - x[-1]*0.05, x[-1]*1.05)
        ax.set_ylim([min(df[col].values)*1.05, max(df[col].values)*1.05])
        ax.tick_params(labelbottom=False, bottom=False, left=False, labelleft=False)
    plt.subplots_adjust(hspace=0, wspace=0, left=0.02, right=0.98, bottom=0.02, top=0.98)
    plt.margins(1, 1)
    c = int(n / nf)
    def animate(i):
        if (i != nf - 1):
            for line, col in zip(lines, df.columns):
                line.set_data(x[:(i+1)*c], df[col].values[:(i+1)*c])
        else:
            for line, col in zip(lines, df.columns):
                line.set_data(x, df[col].values)        
        return lines
    
    anim = FuncAnimation(fig, animate, interval=2000/nf, frames=nf, blit=True)
    


    编辑

    针对cmets,这里是使用问题中更新代码的分块方案的实现:

    x = benchmark_returns.index
    y = benchmark_returns['Crypto 30'] 
    y2 = benchmark_returns['Dow Jones 30']
    y3 = benchmark_returns['NASDAQ'] 
    y4 = benchmark_returns['S&P 500']
    
    line, = ax.plot(x, y, color='k')
    line2, = ax.plot(x, y2, color = 'b')
    line3, = ax.plot(x, y3, color = 'r')
    line4, = ax.plot(x, y4, color = 'g')
    
    n = len(x)  # Total number of rows
    c = 50      # Chunk size
    def update(num):
        end = num * c if num * c < n else n - 1
        line.set_data(x[:end], y[:end])
        line2.set_data(x[:end], y2[:end])
        line3.set_data(x[:end], y3[:end])
        line4.set_data(x[:end], y4[:end])
    
        return line, line2, line3, line4,
    
    ani = animation.FuncAnimation(fig, update, interval = c, blit = True)
    plt.show()
    

    或者,更简洁

    cols = benchmark_returns.columns.values
    # or, for only a subset of the columns
    # cols = ['Crypto 30', 'Dow Jones 30', 'NASDAQ', 'S&P 500']
    colors = ['k', 'b', 'r', 'g']
    lines = []
    for c, col in zip(cols, colors):
        lines.append(ax.plot(benchmark_returns.index, benchmark_returns[col].values, c=c)[0])
    
    n = len(benchmark_returns.index)
    c = 50  # Chunk size
    def update(num):
        end = num * c if num * c < n else n - 1
        for line, col in zip(lines, cols):
            line.set_data(benchmark_returns.index, benchmark_returns[col].values[:end])
    
        return lines
    
    anim = animation.FuncAnimation(fig, update, interval = c, blit=True)
    plt.show()
    

    如果您需要它在一段时间后停止更新,只需在FuncAnimation() 中设置frames 参数和repeat=False

    【讨论】:

    • 我已经尝试过了,但是运行时并不是最好的。似乎随着数据规模的扩大,fps 急剧下降。
    • @HamishGibson 这在某种程度上是不可避免的,当然有办法缓解它(使用blit,使用set_data(),而不是清除和重新绘制等)但仍然在一定范围内无论如何都成为硬件问题。保证 FPS 不受数据大小影响的唯一真正方法是保存动画而不是实时显示。
    • 我设法找到了一个与set_data() 合作的解决方案。但是在这种情况下我将如何使用 clear ?
    • @HamishGibson 我不确定我是否理解您的问题 - 当我说“清除和重新绘制”时,我指的是使用 ax.clearplt.plot 而不是 set_data... ..这就是你要问的吗?
    • @HamishGibson 很高兴为您提供帮助,不要忘记将答案标记为 accepted,以便将来的用户将问题标记为此类。
    【解决方案2】:

    您可以像这样将数据更新到 line 元素中:

    fig = plt.figure()
    ax = fig.add_subplot(111)
    liner, = ax.plot()
    plt.ion()
    plt.show()
    for i in range(len(benchmark_returns.values)):
        liner.set_ydata(benchmark_returns['Crypto 30'][:i])
        liner.set_xdata(benchmark_returns.index[:i])
        plt.pause(0.01)
    

    【讨论】:

    • 我在 衬里中有一个``` 文件“results.py”,第 91 行,= ax.plot() ValueError: no enough values to unpack (expected 1, got 0) ```错误而不是
    • 我设法修复了错误,但运行时间很糟糕,有什么解决方法的提示吗?
    • 如果您可以使用 NumPy 数组而不是 pandas 系列作为输入,您可以看到运行时的潜在改进。
    • 您可能会发现this 很有帮助。还有其他方法可以提供比 matplotlib 更快的时间。
    猜你喜欢
    • 1970-01-01
    • 2021-02-12
    • 2013-08-16
    • 1970-01-01
    • 2016-09-16
    • 1970-01-01
    • 2018-09-20
    • 1970-01-01
    相关资源
    最近更新 更多