Python - Animating large dataset with matplotlib - python

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.

Related

How to properly animate code for a three-body system?

I am trying to write a program that animates the simulated motion of three objects and I have found that the easiest way for me to achieve that was to define the motion of the objects with a looping function that populates the positional arguments a1,a2,a3 for the three different objects. Each "a" is a list of the position of the objects as they move through time; a1=[[x0,y0],[x1,y1],[x2,y2],...]
#Function populating position data#
fig, axes = plt.subplots(nrows = 1, ncols = 1, figsize = (10,10))
axes.set_ylim(-1.5, 1.5)
axes.set_xlim(-1.5, 1.5)
plt.style.use("ggplot")
def animate(j):
axes.plot(a1[0],a1[1], color="red", linewidth=1)
axes.plot(a2[0],a2[1], color="gray", linewidth=0.5)
axes.plot(a3[0],a3[1], color="blue", linewidth=1)
ani = animation.FuncAnimation(fig, animate, interval=10)
plt.show()
For some reason, all I get is a static straight line which doesn't correspond to the positions of any of the objects defined by the position lists a1, a2, or a3. I've combed through the data populating function, and have not found any problems with populating the positions of the objects, so I am assuming that I must have messed up my coding of the animation function. Can someone please lend me a hand and guide me in the right direction?
Edit: I can add the function populating the position data if its needed, but I am just trying to figure out how to animate motion through time along x and y, given positional data.

Matplotlib function animation not animating for pre-calculated data

I am working on some animated scatter plots in python with matplotlib. I currently have this code:
def calulateStep():
# Math stuff ....
# Changes values in in 'circpos' Nx2 array
fig, ax = plt.subplots(figsize=(5, 5))
ax.set(xlim=(-WELLRADIUS,WELLRADIUS), ylim=(-WELLRADIUS,WELLRADIUS))
[x,y] = np.hsplit(circpos,2)
scat = ax.scatter(x.flatten(),y.flatten())
def animate(i):
calculateStep()
scat.set_offsets(circpos)
return scat,
anim = FuncAnimation(fig, animate, frames=60)
anim.save('test2.gif',writer='imagemagick')
plt.draw()
plt.show()
The function calculateStep calculates new x,y values for the scatter. circpos contains the data array at each step. this works well and produces an animated gif ofthe scatter plot as expected. However the function is a rather slow numerical calculation and many many steps are required to produce stable output, so I would rather calculate all before and then animate only select frames. So I tried this.
results = [circpos]
for h in range(61):
calculateStep()
results.append(circpos)
fig, ax = plt.subplots(figsize=(5, 5))
ax.set(xlim=(-WELLRADIUS,WELLRADIUS), ylim=(-WELLRADIUS,WELLRADIUS))
[x,y] = np.hsplit(results[0],2)
scat = ax.scatter(x.flatten(),y.flatten())
def animate(i):
scat.set_offsets(results.pop(0))
return scat,
anim = FuncAnimation(fig, animate, frames=60)
anim.save('test2.gif',writer='imagemagick')
plt.draw()
plt.show()
However with this method the generated gif contains only the final frame of the animation. If I print the data from within the animate function I find that the correct numerical values are being popped from the results list but for some reason only the final value is there in the gif. I have also tried using results[i] rather than results.pop(0) I am at a loss to understand this behavior.
Well it seems I solved my own problem. When I add each iteration of the global array circpos to the results list, it is of course a shallow copy. Meaning it's just a reference to the original circpos array. So I end up with a list full of references to the same object. The print out was just me misinterpreting what I was looking at.
Instead I now add circpos.copy() to my list to get new copies of the array at each step.
This has tripped me up in Python before I realize. Still learning!

Change point color in python plot animation

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

Plot doesn't reset at the end of animation with matplotlib.animation

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

How to animate graph of data in python using matplotlib.animation

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.

Categories