Animating "growing" line plot
Asked Answered
W

1

33

I want to produce a set of frames that can be used to animate a plot of a growing line. In the past, I have always used plt.draw() and set_ydata() to redraw the y-data as it changed over time. This time, I wish to draw a "growing" line, moving across the graph with time. Because of this, set_ydata doesn't work (xdata is changing length). For example,

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.figure()
for n in range(len(x)):
    plt.plot(x[:n], y[:n], color='k')
    plt.axis([0, 10, 0, 1])
    plt.savefig('Frame%03d.png' %n)

While this works, it becomes very slow as it scales. Is there a faster way to do this?

Wyeth answered 21/1, 2015 at 18:41 Comment(7)
Do you have a sample (link) to a graphic you like you're trying to recreate? There are a bunch of animation packages in Python you can use.Continence
@MylesBaker Here is an example of what it would look like (for the code above): media.giphy.com/media/3xz2BD48KS3fOGzAJ2/giphy.gifWyeth
Do you want to update the graph limits as more data is exposed? (I.e. redraw the graph)? Or is your domain and range known?Continence
Domain and range are known. Preferable I would like to set the figure, axis, and labels, then just update the data. If it was possible to just add the newest data point and connect them, that would be fine too, e.g., plot(x[n-1,n], y[n-1,n], color='k').Wyeth
Are you aware of matplotlib's animation.Animate? It can also save a movie.Morril
@Morril I've tried the packages in the past and they've been more of a headache then they're worth. The final output needs to be a gif, so I figured doing frames + concatenating would be quickest. I will take another look at it; thanks!Wyeth
See matplotlib.org/examples/animation/index.html Also, if you have imagemagik installed, mpl will save strait to a gif.Dhahran
P
59

A couple of notes:

First off, the reason that things become progressively slower is that you're drawing more and more and more overlapping lines in the same position.

A quick fix is to clear the plot each time:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.figure()
for n in range(len(x)):
    plt.cla()
    plt.plot(x[:n], y[:n], color='k')
    plt.axis([0, 10, 0, 1])
    plt.savefig('Frame%03d.png' %n)

Better yet, however, update both the x and y data at the same time:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots()
line, = ax.plot(x, y, color='k')

for n in range(len(x)):
    line.set_data(x[:n], y[:n])
    ax.axis([0, 10, 0, 1])
    fig.canvas.draw()
    fig.savefig('Frame%03d.png' %n)

And if you'd like to use the animation module (side note: blit=True may not work properly on some backends (e.g. OSX), so try blit=False if you have issues):

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots()
line, = ax.plot(x, y, color='k')

def update(num, x, y, line):
    line.set_data(x[:num], y[:num])
    line.axes.axis([0, 10, 0, 1])
    return line,

ani = animation.FuncAnimation(fig, update, len(x), fargs=[x, y, line],
                              interval=25, blit=True)
ani.save('test.gif')
plt.show()

enter image description here

Painterly answered 21/1, 2015 at 21:23 Comment(5)
set_data solution is so elegant. Wish I could upvote 10 times.Giselegisella
If saving as video instead of .gif then ani.save('test.mp4', writer='ffmpeg', codec='h264') should replace the last line. If you want to find out which codecs are available then run ffmpeg -codec in the terminal. Given that you want to use ffmpeg as the writer.Etna
Getting this error `ValueError: Invalid file object: <_io.BufferedReader name=8>Caprice
@JoeKington - This might be a silly question, but can I ask where your num arg is coming from? (def update(num, x, y, line)). I don't see it listed in fargs=[x, y, line]. Thanks!Logician
@Joe can you add a dot ahead of the line? I am drawing a line shape in 3d and I want to put a head in front of the line using the last element of the each list, x[-1], y[-1], z[-1]>Skunk

© 2022 - 2024 — McMap. All rights reserved.