Matplotlib: Include certain points during animation - python

I wanted to simulate the trajectory of a projectile which is fired from a certain position and show the highest point and the position where it hits the ground during the animation. My problem is that I can´t figure out how to add these points dynamically while the animation takes place.
Here´s how I animate the path:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation, rc
from IPython.display import HTML
# First set up the figure, the axis, and the plot element we want to animate:
## Get Figure and Axes to plot in
fig, ax = plt.subplots()
## Limits the x- and y-axis
ax.set_xlim((0, 30))
ax.set_ylim((0, 25))
line, = ax.plot([], [], lw=2)
# Setup interval for plotting
interval = np.linspace(0, 29.43, 100)
# Defines the path function of the projectile
def path_of_projectile(x):
return (-(x - 9.81)**2 / 19.62) + 19.62
# Defines the highest point of the projectile
def get_highest_point(x):
return 14.715 + 9.81 * x - 4.905 * (1)**2
# Define the initialization function, which plots the background of each frame:
def init():
line.set_data([], [])
return (line,)
y = path_of_projectile((interval))
# Define the animation function, which is called for each new frame:
def animate(i):
line.set_data(interval[0:i], y[0:i])
return (line,)
# Compile the animation
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20,
blit=True)
# Plot it
HTML(anim.to_jshtml())
The function get_highest_point returns the highest point of the projectile f.e. How can I include it to show up during the animation?

As I also commented, I don't understand the relationship between the function to get the highest point and the Y-value; by indicating the highest point of the Y-value as a point, I think the same technique can be used to handle the highest point obtained from the function. The point is that since the highest point is known before drawing, only the same value as the highest point should be drawn on the graph.
def animate(i):
line.set_data(interval[0:i], y[0:i])
if y[i] == max(y):
ax.scatter(interval[i], y[i], s=25, color='r')
return (line,)

Related

Animating a time-dependent LineCollection using matplotlib

As stated above, I am trying to animate a set of data that varies over time (position). I would like my graph to only show the position data but animate the position history over time. I have started with this example here, and got it working. Now, instead of the whole line animating, I would like for the line to be drawn from left to right. I also need the line to be colored relative to a secondary set of data, which I have been able to accomplish with a LineCollection.
My code:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line = LineCollection([], cmap=plt.cm.jet)
line.set_array(np.linspace(0, 2, 1000))
ax.add_collection(line)
x = np.linspace(0, 2, 10000)
y = np.sin(2 * np.pi * (x))
# initialization function: plot the background of each frame
def init():
line.set_segments([])
return line,
# animation function. This is called sequentially
def animate(i, xss, yss, line):
xs = xss[:i]
ys = yss[:i]
points = np.array([xs, ys]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
line.set_segments(segments)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, fargs=[x, y, line], init_func=init, frames=200, interval=20)
plt.show()
I create a basic sine wave data set and again would like to animate the line being drawn from left to right. Right now, the LineCollection is being colored by the y-value of the line at the current x-position. Eventually, this will be a position data set pulled from a .csv file.
Finally, the issue. The code above runs without errors, however the line is not being drawn. I can see in my debugger that the xs and ys arrays are being added to during each step so that syntax seems to be working, just the updated LineCollection is not being displayed.
I am working on macOS Mojave 10.14.6.
Your code is correct, the line you're plotting is just very small. This is because the function you animate is given by
x = np.linspace(0, 2, 10000) # Note that `num=10000`
y = np.sin(2 * np.pi * (x))
which has 10000 points, but you only animate the first 200 points.
anim = animation.FuncAnimation(..., frames=200, interval=20)
Easy fix
num_frames = 200
x = np.linspace(0, 2, num_frames)
...
anim = animation.FuncAnimation(..., frames=num_frames, interval=20)

Make animation.FuncAnimation() in matplotlib "non-blocking"

I am following the useful advice on this thread:
How to pass arguments to animation.FuncAnimation()?
for passing arguments into maptlotlib.
My issue is I have a device generating (x,y) coordinates at 200Hz.
I would like to plot the x,y position at a slower rate of about 20 Hz.
The issue with my code now is that after getting the first set of (x,y,) points, it goes into the animation.FuncAnimation function and stays there. I am trying to instead keep the (x,y,) data going at 200 Hz and only have the animation.FuncAnimation run at the desired, slower rate.
My code looks like the following:
#!/usr/bin/python
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from matplotlib import style
style.use('dark_background')
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(-1, 1), ylim=(-1, 1))
line, = ax.plot([], [], lw=2)
pointsFollower, = ax.plot([], [], marker='o', color = 'C0',ls="")
def init():
pointsFollower.set_data([], [])
return pointsFollower,
def animate(i,factor):
xFollower = (factor[0])
yFollower = (factor[1])
print("in anim loop:",xFollower,yFollower)
pointsFollower.set_data(xFollower,yFollower)
return pointsFollower,
def main():
while True:
# Wait for the next set of frames from the camera
##function here sets pipeline to device
# Fetch pose frame
##function here gets x and y data.
K = (data.translation.x,data.translation.y)
anim = animation.FuncAnimation(fig, animate, fargs=(K,), init_func=init,
frames=200, interval=20, blit=True)
plt.show()
pipe.stop()
if __name__ == '__main__':
main()

animating a stem plot in matplotlib

I'm trying to animate a stem plot in matplotlib and I can't find the necessary documentation to help me. I have a series of data files which each look like this:
1 0.345346
2 0.124325
3 0.534585
and I want plot each file as a separate frame.
According to this and this other tutorial, I should create a function which updates the data contained in each plot object (artist? I'm not sure about the terminology)
From the second link, this is the update function
def update(frame):
global P, C, S
# Every ring is made more transparent
C[:,3] = np.maximum(0, C[:,3] - 1.0/n)
# Each ring is made larger
S += (size_max - size_min) / n
# Reset ring specific ring (relative to frame number)
i = frame % 50
P[i] = np.random.uniform(0,1,2)
S[i] = size_min
C[i,3] = 1
# Update scatter object
scat.set_edgecolors(C)
scat.set_sizes(S)
scat.set_offsets(P)
# Return the modified object
return scat,
How can I adapt this kind of update function for a stem plot? The documentation for stem is horribly brief (in fact this is a recurring issue as I'm learning matplotlib), but the example code shows that the output of stem is a tuple markerline, stemlines, baseline rather than an artist object like for plt.plot or plt.imshow.
So when I write my update function for the animation, how can I update the data inside the stem plot?
Here you go!
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(0.1, 2*np.pi, 10)
markerline, stemlines, baseline = ax.stem(x, np.cos(x), '-.')
def update(i):
ax.cla()
markerline, stemlines, baseline = ax.stem(x, np.cos(x+i/10), '-.')
ax.set_ylim((-1, 1))
anim = FuncAnimation(fig, update, frames=range(10, 110, 10), interval=500)
anim.save('so.gif', dpi=80, writer='imagemagick')
I think there can be better ways of achieving this- not requiring to clear the plot each time. However, this works!
When using the keyword use_line_collection=True (default behavior since Matplotlib 3.3) one can update the three elements
markerline
stemlines
baseline
individualy. Here is the code for the sine wave example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
x = np.linspace(0.1, 2*np.pi, 10)
y = np.cos(x)
bottom = 0
h_stem = ax.stem(x, y, bottom=bottom, use_line_collection=True, linefmt='-.')
def update(i):
y = np.cos(x+i/10)
# markerline
h_stem[0].set_ydata(y)
h_stem[0].set_xdata(x) # not necessary for constant x
# stemlines
h_stem[1].set_paths([np.array([[xx, bottom],
[xx, yy]]) for (xx, yy) in zip(x, y)])
# baseline
h_stem[2].set_xdata([np.min(x), np.max(x)])
h_stem[2].set_ydata([bottom, bottom]) # not necessary for constant bottom
anim = FuncAnimation(fig, update, frames=range(10, 110, 10), interval=1)
anim.save('so.gif', dpi=80, writer='imagemagick')
Depending on what values (x, y, bottom) should be updated you can omit some parts of this update or reuse the current values. I wrote a more general function, where you can pass an arbitrary combination of these values:
def update_stem(h_stem, x=None, y=None, bottom=None):
if x is None:
x = h_stem[0].get_xdata()
else:
h_stem[0].set_xdata(x)
h_stem[2].set_xdata([np.min(x), np.max(x)])
if y is None:
y = h_stem[0].get_ydata()
else:
h_stem[0].set_ydata(y)
if bottom is None:
bottom = h_stem[2].get_ydata()[0]
else:
h_stem[2].set_ydata([bottom, bottom])
h_stem[1].set_paths([np.array([[xx, bottom],
[xx, yy]]) for (xx, yy) in zip(x, y)])

Python multi-body animation does not work

I am stuck with a python animation in which I am trying to animate a system of particles initially arranged in a 2 dimensional hexagonal lattice and gradually spreading out as per rule: xpos1[i]=xpos1[i]+L/10.0. If any particle goes out of the window limit, they are brought in through the other side
if xpos1[i]>L*3: # translate back the particle if it goes out of window limit 0 to L*3
xpos1[i]=xpos1[i]-L*3
elif xpos1[i]<0:
xpos1[i]=L*3-xpos1[i]
And all the updates of position are stored in two list xpos1 and ypos1. This is done for several time steps.
I wish to visualize the time evolution of the system by turning it to an animation. My code is as follows. I have never done matplotlib animations before and actually copied the 'animation' part from another program where it works fine. But it does not work for mine.
from numpy import*
import matplotlib.pyplot as plt
import matplotlib.animation as animation
sigma=3.4e-10 # dist of closest approach
L=4.8e-10 # lattice constant = sigma*2**0.5 (Let)
xpos1=zeros(18,float)
ypos1=zeros(18,float)
# ~~~~~~~~~~~ Setting up the hexagonal lattice ~~~~~~~~~~~~~~~~~~~~~~
k=0
for x in range(0,6,1):
for y in range(0,6,1):
if (x+y)%2==0:
xpos1[k]=x*L*.5+.25*L
ypos1[k]=y*L*.5+.25*L
k=k+1
#~~~~~~~~~~~~~~~~~~TIME EVOLUTION~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t = 4.5e-12
iteration=1
while t<=1e-9:
for i in range(18):
xpos1[i]=xpos1[i]+L/10.0
ypos1[i]=ypos1[i]+L/10.0
if xpos1[i]>L*3: # translate back the particle if it goes out of window limit 0 to L*cell
xpos1[i]=xpos1[i]-L*3
elif xpos1[i]<0:
xpos1[i]=L*3-xpos1[i]
if ypos1[i]>L*3: # translate back the particle if it goes out of window limit 0 to L*cell
ypos1[i]=ypos1[i]-L*3
elif ypos1[i]<0:
ypos1[i]=L*3-ypos1[i]
t = t + 4.5e-12
#~~~~~~~~~~~~~~~~~ ANIMATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def simData():
for i in range(18):
x=xpos1[i]
y=ypos1[i]
yield x,y
def simPoints(simData):
x,y= simData[0],simData[1]
line.set_data(x,y)
return line
fig = plt.figure()
ax = fig.add_subplot(111)
line,= ax.plot([],[],'bo',ms=8)
ax.set_ylim(0 , L*3)
ax.set_xlim(0 , L*3)
ani = animation.FuncAnimation(fig, simPoints, simData, blit=True , interval=200)
plt.show()
Can somebody tell me how to make the animation successfully?
Your animation update (and init if you have one) must return an iterable.
def simPoints(simData):
x, y = simData[0], simData[1]
line.set_data(x, y)
return line, # added a comma to return a tuple
You may also need to set blit=False if you are on mac os
ani = animation.FuncAnimation(fig, simPoints, simData, blit=False, interval=200)
Edit:
Here is a minimum working example that shows 18 random points - you'll have to change the random generation to the pattern you want for the points on your lattice.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.random.random(18)
y = np.random.random(18)
def simData():
"""updates the points position on your lattice.
replace with your own code - can call a helper function to accomplish this task
"""
x = np.random.random(18)
y = np.random.random(18)
yield x, y
def simPoints(simData):
"""initializes the points position on your lattice.
replace with your own code - can call a helper function to accomplish this task
"""
x = np.random.random(18)
y = np.random.random(18)
line.set_data(x, y)
return line,
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot(x, y,'bo', ms=8)
ani = animation.FuncAnimation(fig, simPoints, simData, blit=False, interval=200)
plt.show()

Using Matplotlib-Patch inside an animation

I try to generate an empty patch to be able to set data later on. In order to explain my problem better, i will give an example:
from matplotlib import pyplot as plt
import matplotlib.animation as animation
x = range(10)
y = [i**2 for i in x]
figure = plt.figure()
ax1 = figure.add_subplot(111, xlim=(0,10), ylim=(0,100))
my_line, = ax1.plot([],[], 'o-')
def init():
my_line.set_data([], [])
return my_line,
i = 0
def animate(_):
global i
my_line.set_data(x[0:i], y[0:i])
i = (i+1)%(len(x)+1)
return my_line,
ani = animation.FuncAnimation(figure, animate, repeat=True, blit=True, init_func=init)
plt.show()
Now, I to add a shape, which I define its edge points randomly. I need to use the same structure as I used for plotting lines inside the init() block: my_line.set_data([], []) . However, I couldn't succeed.
I use the same structure as the example provided in the matplotlib tutorial . My verts are generated from a function.
When I try using: foo = patches.PathPatch([], facecolor='red', lw=2, alpha=0.0) I get
<matplotlib.patches.PathPatch at 0x335d390>
But later, I cannot set the path data. I tried using foo.set_data and foo.set_path but PathPatch doesn't have such attributes and therefore, they didn't work. I checked this page but I couldn't get anywhere. I checked all of the tutorials I could find, but none of them helped.
As a workaround, I used ax1.add_patch() command and have set the alpha value to 0. This helped to some extend but, as I have to enter data to be able to use this command, all of the shapes become visible at the final step of the animation for a very short time and, as I save my figure in that moment, it yields unfavorable results.
Any help would be appreciated...
I'm not sure what shape you're using, if you use a polygon, you can update the vertices of a polygon with the set_xy method and create the initial polygon with vertices that are all equal to each other. Example below. If you need a completely arbitrary shape, you might be better off plotting lines and using fill_between to draw it.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
# Create the figure and axis
fig = plt.figure()
ax = plt.axes(xlim=(0, 10), ylim=(0, 10))
# mMke the initial polygon with all vertices set to 0
pts = [[0,0], [0,0], [0,0], [0,0]]
patch = plt.Polygon(pts)
ax.add_patch(patch)
def init():
return patch,
def animate(i):
# Randomly set the vertices
x1= 5*np.random.rand((1))[0]
x2= 5*np.random.rand((1))[0]
x3= 5*np.random.rand((1))[0] + 5
x4= 5*np.random.rand((1))[0] + 5
y1= 5*np.random.rand((1))[0]
y2= 5*np.random.rand((1))[0]
y3= 5*np.random.rand((1))[0] + 5
y4= 5*np.random.rand((1))[0] + 5
patch.set_xy([[x1,y1], [x2,y2], [x3,y3], [x4,y4]])
return patch,
anim = animation.FuncAnimation(fig,animate,init_func=init,frames=36,interval=1000,blit=True)
plt.show()

Categories

Resources