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

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.

Related

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!

How to plot an histogram or bar animation with matplotlib ArtistAnimation?

I'm trying to create histogram animation that stacks up samples one by one.
I thought the following code would work, but it doesn't.
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
ims = []
fig = plt.figure()
x =[1,2,3,3,4,5,5,5,5,6,7,8,9,9,9,9,9,10]
for i in range(len(x)):
img = plt.hist(x[:i])
ims.append(img)
ani = ArtistAnimation(fig, ims, interval=100)
plt.show()
Changing plt.hist to plt.plot shows animation. Don't know what the difference is.
Thanks for reading.
plt.hist does not return an artist but a tuple with binning results and the artist (see here).
This is also discussed in this thread in the Matplotlib mailing list:
ArtistAnimation expects to be given a list of lists (or tuples), where
the inner collection contains all of the artists that should be
rendered for a given frame. In the case of bar, it returns a
BarCollection object (which I just learned), is a subclass of tuple.
This explains why it works (by itself), when directly appended to the
list given to ArtistAnimation; the BarCollection acts as the
collection of artists that ArtistAnimation is expecting. In the case
of the second example, ArtistAnimation is being given a
list([BarCollection, Image]); because BarCollection isn't actually an
Artist, it causes the problem.
The problem mentioned there uses different plot types:
im1 = ax1.imshow(f(xm, ym), aspect='equal', animated=True)
im2 = ax2.bar(xx, a, width=0.9*(b[1]-b[0]), color='C1')
and the solution in that case is
ims.append([im1] + list(im2))
The way to make this work in your case is to look at the return values of plt.hist and find out where the artists are being returned and put them in the correct list of lists format.
This works:
import matplotlib.animation as animation
fig = plt.figure()
# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are just animating one artist, the image, in
# each frame
ims = []
for i in range(60):
# hist returns a tuple with two binning results and then the artist (patches)
n, bins, patches = plt.hist((np.random.rand(10),))
# since patches is already a list we can just append it
ims.append(patches)
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000,)
plt.show()

Python - Animating large dataset with matplotlib

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.

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.

Matplotlib: Animate multiple scatter plots

I'm trying to create an animation which shows multiple particles moving around.
If I have one particle with one array giving the positions of that particle in each step of the animation, I get it to work (mostly thanks to extensive help from other answers I found here on stackoverflow).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
positions = np.array([[2,2],[3,3],[4,4]])
def init():
scatterplot.set_offsets([[], []])
return [scatterplot]
def update(i, scatterplot, positions):
scatterplot.set_offsets(positions[i])
return [scatterplot]
fig = plt.figure()
scatterplot = plt.scatter([], [], s=100)
plt.xlim(0,5)
plt.ylim(0,5)
anim = animation.FuncAnimation(
fig, update, init_func=init, fargs=(scatterplot, positions), interval=1000, frames=3,
blit=True, repeat=True)
plt.show()
But I cannot figure out how to add more particles to the same animation.
Let's say I want to add a second particle with positions
positions2 = np.array([[2,1][3,2][4,3]])
and have it move around in the same scatter plot, how do I manage that?
I'm a matplotlib newbie, and have been googling furiously to no avail, will be very grateful for any help :)
EDIT:
I figured it out eventually, just a matter of formatting the data correctly.
positions = np.array([[[2,2],[2,1]],[[3,3],[3,2]],[[4,4],[4,3]]])
Where the array contains all the positions in step one, then all the positions in step two etc. works.
I'd prefer to get one color pr moving point, to keep track of them, but at least it works now.
I figured it out eventually, just a matter of formatting the data correctly.
positions = np.array([[[2,2],[2,1]],[[3,3],[3,2]],[[4,4],[4,3]]])
Where the array contains all the positions in step one, then all the positions in step two etc. works.
I'd prefer to get one color pr moving point, to keep track of them, but at least it works now.

Categories