I'm using matplotlib.animation to create a 4 bar linkage. in the arrX# arrY# variables I have the x&y of the lines (360 values in each). I'm using the standard template from the matplotlib website.
During one loop where variable frame goes from 0 to 359 everything is fine. But when it goes for a second loop, the image at the end (frame=359) of the last loop stays on the plot. At the end of every next plot it is drawn again and again.
How can I reset the plot or what can I do to eliminate the old lines at the end of the loop?
Code is below.
Image shows two sets of lines, where one set of lines is static that doesn't go away and is redrawn again and again at the end of each loop.
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
ln1, = plt.plot([], [], '-r')
ln2, = plt.plot([], [], '-b')
ln3, = plt.plot([], [], '-g')
def init():
ax.set_xlim(-100, 300)
ax.set_ylim(-100, 200)
return ln1,ln2,ln3
def update(frame):
xdata=(arrX2[frame])
ydata=(arrY2[frame])
ln1.set_data([0,xdata], [0,ydata])
ln2.set_data([xdata,arrX3[frame]],[ydata,arrY3[frame]])
ln3.set_data([d,arrX3[frame]],[0,arrY3[frame]])
return ln1,ln2,ln3
ani = FuncAnimation(fig, update, frames=range(0,360,1), init_func=init, blit=True,interval=1)
plt.show()
When I was creating a "Verifiable example" i found the problem.
Originally my init() function was this:
def init():
ax.set_xlim(-100, 300)
ax.set_ylim(-100, 200)
return ln1,ln2,ln3
This is wrong. This function should only hold the empty objects so that the animator can look into here and see what objects will need to be updated on each frame in the update() function.
Otherwise, in my example, once the "video reel" came to an end, and the time for the re-initialization came up, it simply plotted the last known line objects.
In the init() function I only kept the empty line objects. This worked.
def init():
ln1, = plt.plot([], [], '-r')
ln2, = plt.plot([], [], '-b')
ln3, = plt.plot([], [], '-g')
return ln1,ln2,ln3,
I'm convinced that this was an error. If not I just don't know.
P.S. Not sure if this needs to be kept here or not since this was a syntax error. I leave it up to more experienced users to decide :)
Related
I have a list of points, lets say as (x,y) pairs. I am trying to animate a plot so that each frame of the animation, a new point show up on the plot in a different color. Specifically on the 0th frame, the 0th point appears, on the the 1st frame, the 1st point appears, and so on. I would also like to have these points appear in a new color, specifically like a linear progression through a color palette as the points progress, so that you can "follow" the points by their color. This is similar to, and how I got as far as I am now: How can i make points of a python plot appear over time?. The first animation in the link is spot on, except without the points changing colors.
I am using matplotlib, matplotlib.pyplot, and FuncAnimation from matplotlib.animation
What I have already:
def plot_points_over_time(list_of_points):
num_points = len(list_of_points)
fig = plt.figure()
x, y = zip(*list_of_points)
plt.xlim(min(x),max(x))
plt.ylim(min(y),max(y))
colors = [plt.cm.gist_rainbow(each) for each in np.linspace(0,1,num_points)]
graph, = plt.plot([],[],'o')
def animate(i):
graph.set_data(x[:i+1],y[:i+1])
return graph
ani = FuncAnimation(fig, animate, frames = num_points, repeat = False, interval = 60000/num_points)
plt.show()
I can change the color of all of the points together on each frame by including the line graph.set_color(colors[i]) in the animate function, but not each point individually.
Figured it out with some digging and trial and error:
def plot_points_over_time(list_of_points):
num_points = len(list_of_points)
fig = plt.figure()
x, y = zip(*list_of_points)
plt.xlim(min(x),max(x))
plt.ylim(min(y),max(y))
colors = [plt.cm.gist_rainbow(each) for each in np.linspace(0,1,num_points)]
scat, = plt.plot([],[])
def animate(i):
scat.set_offsets(np.c_[x[:i+1], y[:i+1]])
scat.set_color(colors[:i+1])
return scat,
ani = FuncAnimation(fig, animate, frames = num_points, repeat = False, interval = 60000/num_points)
plt.show()
I have an existing function I use for plotting, which I call repeatedly in my program.
I want to use matplotlib's ArtistAnimation to save each plot as an "artist" that is shown in one step of the animation.
I know how to use ArtistAnimation to show individual elements of the plot in the animation, but not the entire plot.
Here's a simplified example:
import random
def my_plot():
fig, ax = plt.subplots()
ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
plt.show()
return ax
ims = []
fig = plt.figure()
for _ in range(5):
ax = my_plot()
ims.append((ax,))
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})
This runs without error, but the resulting video is just blank. The same happens if I return a list of the artists created by ax.plot().
I assume the problem is that I'm calling plt.figure/plt.subfigure multiple times. But I'm not sure how to avoid that. Do I need to create one figure up front and pass that to each call of my_plot? Seems a bit ugly.
Instead of saving the axes, you need to save the plots as a list. (Or maybe you don't want to do this and want to save the axes? If that's the case, let me know and I'll delete this. I don't think saving the axes will work though, since the animation works by setting the saved items within a figure visible and invisible, and neither the axes nor the figure will hide/reveal a subset of the plots for each frame in this way.)
import matplotlib.pyplot as plt
from matplotlib import animation
import random
def my_plot(ax):
p0, = ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
p1, = ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
return [p0, p1] # return a list of the new plots
ims = []
fig = plt.figure()
ax = fig.add_subplot(111) # fig and axes created once
for _ in range(10):
ps = my_plot(ax)
ims.append(ps) # append the new list of plots
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})
GIF below, but here is some vertical spacing so you can scroll the annoying flashing lines of the page while reading the code
. . . . . . . . . . . . . .
Thanks to tom's answer, I found the main reasons why my animations didn't work and only showed the first frame: I called plt.show() in each iteration. Apparently, after the first call, the animations stop working. Removing plt.show() and only creating one figure solved the problem:
import matplotlib.pyplot as plt
from matplotlib import animation
import random
def my_plot():
patch = []
patch.extend(plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)]))
patch.extend(plt.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)]))
# no plt.show() here!
return patch
ims = []
fig = plt.figure() # fig created only once
for _ in range(10):
patch = my_plot()
ims.append(patch)
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})
Not sure how I could both plot and show the plots directly and create an animation. Maybe using plt.draw() instead? But that doesn't show anything in my PyCharm IDE...
Anyways, I can live with either or.
For a personal project, I'm trying to animate a fairly large data set (1000 rows) to show multiple bird dives in Jupyter notebook. Eventually I'd also like to add subplots of acceleration data along with it.
I used simple examples as a rough template, such as the growing coil example in: https://towardsdatascience.com/animations-with-matplotlib-d96375c5442c
The code itself seems to run slow but fine, however it doesn't output an animation, just a static graph:
Here's my current code:
x = np.array(dives.index)
y = np.array(dives['depth'])
x_data, y_data = [], []
fig = plt.figure()
ax = plt.axes(xlim=(0, 1000), ylim=(min(y),max(y)))
line, = ax.plot([], [])
def init():
line.set_data([], [])
return line,
def animate(i):
x_data.append(x[i])
y_data.append(y[i])
line.set_data(x, y)
return line,
plt.title('Bird Dives')
ani = animation.FuncAnimation(
fig, animate, init_func=init, frames= 1000, interval=50, blit=True)
ani.save('./plot-test.gif')
plt.show()
Is there a reason why it's just plotting a graph rather than an animated one?
Yes, your error is in your animate function. You have line.set_data(x, y), which is plotting the entire contents of x and y at every frame (and hence produces an animated graph that doesn't change).
What you intended to have in your animate function was line.set_data(x_data, y_data).
As for performance: you can improve this by not creating an empty list and appending to it at every iteration. Instead its simpler to slice your original arrays x and y. Consider the following animate function instead:
def animate(i):
line.set_data(x[:i], y[:i])
return line,
Having said this, given that you've got a thousand frames it's still going to take a little while to run.
I have two arrays x and y, each one has more than 365000 elements. I would like to draw an animated line using these array elements. I'm using matplotlib.animation for it. Problem is when I execute the code below I can't see the graph smoothly(animated) drawed. Contrary I see it's final drawed version.
Here is my code:
#libs
# Movement instance creation-----------------------------
movement1=Movement(train1, track1)
# # Move the train on the track
movement1.move()
y = movement1.speed
x = movement1.pos
Writer = animation.writers['ffmpeg']
writer = Writer(fps=20, metadata=dict(artist='Me'), bitrate=1800)
fig = plt.figure()
ax = plt.axes(xlim=(0, 25), ylim=(0, 300))
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=200, blit=True)
anim.save('basic_animation.mp4', writer=writer)
Here is the similar result that I expect:
Of course my graph would be another curve.
Your code is mostly fine; you just need to do three things.
Set the xdata and ydata of the line to different values every iteration of anim_func (otherwise, there would be no animation, would there?)
Set constant axis limits so your plot doesn't change shape
Remove the save call for display purposes (for me personally, I find it affects the animation)
So:
ax.axis((x.min(), x.max(), y.min(), y.max())
def animate(i):
line.set_data(x[:i], y[:i])
return line,
You need to define a set of data that is changing for animation to occur. In the example site you gave, the author does it by slicing the data using overdose.iloc[:int(i+1] (see below for the actual code used). This is the part that creates the animation as matplotlib plots whatever data is in the animate function. In your code you have input line.set_data(x, y) which I suppose is your entire dataset. That's why it isn't moving.
def animate(i):
data = overdose.iloc[:int(i+1)] #select data range
p = sns.lineplot(x=data.index, y=data[title], data=data, color="r")
p.tick_params(labelsize=17)
plt.setp(p.lines,linewidth=7)
Second thing to note is that your plot is getting chopped off at the top. That is likely because your initialisation is already setting the axis wrongly. What I would do is to add in a plt.axis([0, 25, 0, 'upper limit']) to help set the axis correctly.
I wrote a code that generates and shows a matplotlib animation. I am also able to save the animation as gif.
However when I do these two actions sequentially (first saving the gif, and then showing the animation, because plt.show() is a blocking call, the shown animation doesn't start from the beginning; it seems it's first used by the gif saving routine.
My idea is to show the animation in the window using plt.show(), and save the same animation as a gif to disk (before, or ideally during the animation). I've tried to solve this using Threads or creating new figure, but I wasn't able to pull it off - the shown animation was always modified by the saving routine. Also, putting plt.show into a thread didn't work (because it handles drawing to screen, I suppose, so it needs to be in a main thread).
Here's the code I'm using:
import matplotlib.pyplot as plt
from matplotlib import animation
from visualizer import visualize
import updater
def run(k, update_func=updater.uniform,
steps=1000, vis_steps=50,
alpha_init=0.3, alpha_const=100, alpha_speed=0.95,
diameter_init=3, diameter_const=1, diameter_speed=1,
xlim=(-1, 1), ylim=(-1, 1),
points_style='ro', lines_style='b-',
save_as_gif=False):
fig = plt.figure()
ax = fig.add_subplot(111, xlim=xlim, ylim=ylim)
global curr_alpha, curr_diameter, points, lines
points, = ax.plot([], [], points_style)
lines, = ax.plot([], [], lines_style)
curr_alpha = alpha_init
curr_diameter = diameter_init
def update_attributes(i):
global curr_alpha, curr_diameter
if i % alpha_const == 0:
curr_alpha *= alpha_speed
if i % diameter_const == 0:
curr_diameter *= diameter_speed
def init():
lines.set_data([], [])
points.set_data([], [])
def loop(i):
for j in range(vis_steps):
update_func(k, curr_alpha=curr_alpha, curr_diameter=curr_diameter)
update_attributes(i * vis_steps + j)
visualize(k, points, lines)
return points, lines
anim = animation.FuncAnimation(fig, loop, init_func=init, frames=steps // vis_steps, interval=1, repeat=False)
if save_as_gif:
anim.save('gifs/%s.gif' % save_as_gif, writer='imagemagick')
plt.show()