Как сделать динамическое построение c matplotlib с фиксированным pandas кадром данных? - PullRequest
1 голос
/ 09 марта 2020

У меня есть датафреймы с именами benchmark_returns и strategy_returns. Оба имеют одинаковое время. Я хочу найти способ построить точки данных в хорошем стиле анимации, чтобы он показывал все точки загрузки постепенно. Я знаю, что существует matplotlib.animation.FuncAnimation(), однако обычно он используется только для обновления в реальном времени файлов CSV и т. Д. c, но в моем случае я знаю все данные, которые хочу использовать.

Я также пытался использовать грубый метод 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()

Ответы [ 2 ]

1 голос
/ 10 марта 2020

Вы можете попробовать matplotlib.animation.ArtistAnimation. Он работает аналогично FuncAnimation в том смысле, что вы можете указать интервал кадра, циклическое поведение и т. Д. c, но все графические операции выполняются сразу, до шага анимации. Вот пример

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)

enter image description here

Недостатком здесь является то, что каждый художник либо нарисован, либо нет, то есть вы не можете рисовать только часть Line2D объект без отсечения. Если это не совместимо с вашим сценарием использования, вы можете попробовать использовать FuncAnimation с blit=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)

enter image description here


Редактировать

В ответ на комментарии Вот реализация схемы разбиения на блоки с использованием обновленного кода в вопросе:

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

, и, если вам нужно, прекратить обновление после определенного периода время просто установить аргумент frames и repeat=False в FuncAnimation().

1 голос
/ 10 марта 2020

Вы можете просто обновить данные в элемент строки следующим образом:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...