UPDATE: It is an issue getting the animation to compile and run within Jupyter itself. As an easy workaround, I am just going to compile animations in IDLE, save them as a gif, then import them into my notebook via markdown.
I am trying to animate different pendulum systems within a Jupyter notebook. I am solving the differential equations for the systems, then creating a list of angle values to plot versus time (that part is easy ;). I have created a dummy list of theta values to simplify things for this post. The first goal is to get a line with a fixed point to sway back and forth (like a pendulum), eventually adding a mass on the end of it for the aesthetics. Here is my code:
%matplotlib inline
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
l = 0.25 # length of pendulum
# create a list of angles, convert to radians
theta_points_approx = [5.0,4.8,4.2,3.2,2.1,1.9,0.2,-1.2,-2.2,-3.1,-3.9,-4.6,-5.0]
theta_points_approx_rad = []
for ang in theta_points_approx:
theta_points_approx_rad.append(ang*np.pi/180)
# create blank window for animation
fig = plt.figure()
axis = plt.axes()
pend_approx, = axis.plot([], [], lw=2)
#create a function to draw the background of our animations
def init():
pend_approx.set_data([], [])
return(pend_approx,)
# initialize empty sets for x and y coordinates
x_points_to_plot = []
y_points_to_plot = []
# animation function
def animate(theta):
x = [0,l*np.sin(theta)]
y = [0,-l*np.cos(theta)]
pend_approx.set_data(x,y)
return(pend_approx,)
# compile animation
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=theta_points_approx_rad, interval=40, blit=True)
plt.show()
This just outputs an empty plot. I have spent the evening watching YT videos and reading through animation examples trying to get this solved, but to no avail. I am not sure what to even try from here. Any advice would be greatly appreciated.
Edit: It was not because I didn't have the show() function at the end. Even with show I still get an empty plot
Related
I am trying to create an animation containing a fixed sphere and a trajectory on the surface of the sphere, with a trail of the trajectory containing the last "windowSize" points in the trajectory.
Now, for the purposes of the code I will show here, I won't have an actual such trajectory, but rather just some random points changing each frame.
I am using matplotlib.animation.FuncAnimation. When I use the option blit=False, the animation works as expected. However, I would like to use blit=True to optimize performance.
When I do that, though, what happens is that nothing seems to happen in the animation, except that when I rotate the figure, then it shows an updated version of the figure (some number of frames ahead) and then freezes again.
The code below is based on this similar question.
Let me show the code I am using
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.animation
import pandas as pd
Np = 5000
windowSize = 1000
m = np.random.rand(Np, 3)
df = pd.DataFrame({ "x" : m[0:Np,0], "y" : m[0:Np,1], "z" : m[0:Np,2]})
def init_graph():
u, v = np.mgrid[0:2*np.pi:50j, 0:np.pi:50j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
z = np.cos(v)
ax.plot_surface(x, y, z, color="bisque", alpha=0.3)
return graph,
def update_graph(num):
if (num<windowSize):
graph._offsets3d = (df.x[0:num], df.y[0:num], df.z[0:num])
else:
graph._offsets3d = (df.x[(num-windowSize):num], df.y[(num-windowSize):num], df.z[(num-windowSize):num])
title.set_text('3D Test, time={}'.format(num))
return graph,
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_box_aspect((1,1,1))
title = ax.set_title('3D Test')
graph = ax.scatter(0, 0, 0)
ani = matplotlib.animation.FuncAnimation(fig, update_graph, frames=Np, init_func=init_graph, interval=200, blit=True, repeat=False)
plt.show()
m is an Np by 3 matrix, and each row represents a 3d point (in my real use case, each row is a point in a trajectory on the sphere surface, but for this demo I created m as random numbers).
I create a variable graph that contains a scatter plot, which I believe is an Artist. This is what I return from both the init_func and the updating func which are passed to FuncAnimation (as per the docs).
From what I read, you return an iterable of the Artists which will be updated in the animation. Thus I return a tuple of one element, graph,.
Now, in update_graph, the updating function for the animation, I am updating the scatter plot using graph._offsets3d, which I read in another question here on StackOverflow. I am not totally sure if this is the way to do it and I didn't find much information in the docs about whether to use this or one of the setting methods on the scatter plot.
Why doesn't blitting work with scatter plots?
I am making a simple animated scatter plot in a Jupyter notebook, and I want it to repeat, but with a delay before it loops. This is supposed to be set with the repeat_delay parameter, but it has no effect when I use it in a Jupyter notebook using HTML(ani.to_html5_video()) to show the animation.
Here is a simple example of repositioning 20 points every 200 ms, but trying to add a 2 second delay before repeating the animation:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from IPython.display import HTML
frame_interval = 200
vid_repeat = True
repeat_delay = 2000 # set long so it would be obvious
def update_xy(i, scat):
scat.set_offsets(np.random.random((20,2)))
return scat,
fig = plt.figure()
init_data = np.random.random((20,3))
scat = plt.scatter(init_data[:,0], init_data[:,1], c=init_data[:,2], s=50, cmap = "hot")
ani = animation.FuncAnimation(fig,
update_xy,
frames=numframes,
interval = frame_interval,
repeat = vid_repeat,
repeat_delay = repeat_delay,
fargs=(scat,))
plt.close(ani._fig)
HTML(ani.to_html5_video())
At the end, it just loops around at frame_interval no matter what value I put for repeat_delay. I get the same result when I save the animation ani.save('foo.mp4'), or try to play it using HTML(ani.to_jshtml()).
Related Questions
Animation in iPython notebook
How to animate a scatter plot?
I am trying to animate some fractals in matplotlib using FuncAnimation.
When I have the blit set to False I get no errors: the code runs fine and generates a nice animation for me. However, when I set the blit to True, it gives me TypeError: 'Line2D' object is not iterable. Does anyone know why this happens and how I can fix it?
I would like to take advantage of blitting as I am planning to animate a large family of fractals and just taking a small slice of them (64 different fractals) already takes noticeable computation time. I have a fast way to generate a matrix with columns containing the different fractals so I know computation time is spent trying to animate a bunch of plots without blitting.
In my example I am just animating the iteration of generating a fractal. This is a short and fast way of illustrating the error I am getting, not what I am actually trying to animate because otherwise I wouldn't care about the blitting.
Here is a minimal example that should run in a jupyter notebook if you have ffmpeg installed:
import numpy as np
import scipy as sp
import scipy.linalg as la
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib import animation, rc
from IPython.display import HTML
%matplotlib inline
plt.rcParams['figure.figsize'] = [8,8]
plt.rcParams['animation.ffmpeg_path'] = "C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe" #<- CHANGE TO YOUR PATH TO FFMPEG or delete this line if the notebook knows where to find ffmpeg.
class IFS:
"""Represent an iterated function system used to generate a fractal."""
def __init__(self,start,f1,f2,reversef2=False):
self.points = start
self.f1 = f1
self.f2 = f2
self.reversef2 = reversef2
def iterate(self,iterations=1):
"""Perform iterations using the functions"""
for i in range(0,iterations):
if self.reversef2: #This is needed for the dragon curve
self.points = np.append(self.f1(self.points),self.f2(self.points)[::-1])
else: #However, you do not want to append in reverse when constructing the levyC curve
self.points = np.append(self.f1(self.points),self.f2(self.points))
def get_points(self):
return self.points
def dragon_ifs():
"""Return a dragon fractal generating IFS"""
def f1(z):
return (0.5+0.5j)*z
def f2(z):
return 1 - (0.5-0.5j)*z
return IFS(np.array([0,1]),f1,f2,reversef2=True)
#Animation
class UpdateFractal:
"""A class for animating an IFS by slowly iterating it"""
def __init__(self,ax,ifs):
self.ifs = ifs
self.line, = ax.plot([], [], 'k-',lw=2)
self.ax = ax
#set parameters
self.ax.axis([-1,2,-1,2])
def get_plot_points(self):
"""Get plottable X and Y values from the IFS points"""
points = self.ifs.get_points()
X = points.real
Y = points.imag
return X,Y
def init(self):
X,Y = self.get_plot_points()
self.line.set_data(X,Y)
return self.line
def __call__(self,i):
self.ifs.iterate()
X,Y = self.get_plot_points()
self.line.set_data(X,Y)
return self.line
fig, ax = plt.subplots()
dragon = dragon_ifs()
uf = UpdateFractal(ax,dragon)
anim = FuncAnimation(fig, uf, frames=np.arange(15), init_func=uf.init,
interval=200, blit=True)
#Show animation
HTML(anim.to_html5_video())
Side Note: I saw this also unanswered question: FuncAnimation not iterable and I think they may be facing the a similar problem. I noticed they have blit set to True and if I had enough reputation I would comment and ask them if setting blit to False "fixes" their problem.
As the error suggests, the return of the animating function needs to be an iterable.
Replace the line return self.line by
return self.line,
(note the comma)
I have a python / matplotlib application that frequently updates a plot with new data coming in from a measurement instrument. The plot window should not change from background to foreground (or vice versa) with respect to other windows on my desktop when the plot is updated with new data.
This worked as desired with Python 3 on a machine running Ubuntu 16.10 with matplotlib 1.5.2rc. However, on a different machine with Ubuntu 17.04 and matplotlib 2.0.0, the figure window pops to the front every time the plot is updated with new data.
How can I control the window foreground/background behavior and keep the window focus when updating the plot with new data?
Here's a code example illustrating my plotting routine:
import matplotlib
import matplotlib.pyplot as plt
from time import time
from random import random
print ( matplotlib.__version__ )
# set up the figure
fig = plt.figure()
plt.xlabel('Time')
plt.ylabel('Value')
plt.ion()
# plot things while new data is generated:
t0 = time()
t = []
y = []
while True:
t.append( time()-t0 )
y.append( random() )
fig.clear()
plt.plot( t , y )
plt.pause(1)
matplotlib was changed somewhere from version 1.5.2rc to 2.0.0 such that pyplot.show() brings the window to the foreground (see here). The key is therefore to avoid calling pyplot.show() in the loop. The same goes for pyplot.pause().
Below is a working example. This will still bring the window to the foreground at the beginning. But the user may move the window to the background, and the window will stay there when the figure is updated with new data.
Note that the matplotlib animation module might be a good choice to produce the plot shown in this example. However, I couldn't make the animation work with interactive plot, so it blocks further execution of other code. That's why I could not use the animation module in my real-life application.
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import time
from random import random
print ( matplotlib.__version__ )
# set up the figure
plt.ion()
fig = plt.figure()
ax = plt.subplot(1,1,1)
ax.set_xlabel('Time')
ax.set_ylabel('Value')
t = []
y = []
ax.plot( t , y , 'ko-' , markersize = 10 ) # add an empty line to the plot
fig.show() # show the window (figure will be in foreground, but the user may move it to background)
# plot things while new data is generated:
# (avoid calling plt.show() and plt.pause() to prevent window popping to foreground)
t0 = time.time()
while True:
t.append( time.time()-t0 ) # add new x data value
y.append( random() ) # add new y data value
ax.lines[0].set_data( t,y ) # set plot data
ax.relim() # recompute the data limits
ax.autoscale_view() # automatic axis scaling
fig.canvas.flush_events() # update the plot and take care of window events (like resizing etc.)
time.sleep(1) # wait for next loop iteration
For the tkinter backend (matplotlib.use("TkAgg")), using flush_events is not sufficient: you also need to call fig.canvas.draw_idle() before each fig.canvas.flush_events(). As #samlaf wrote, the same holds for the Qt5Agg backend.
I'm using pyplot 2.0 with the FuncAnimation for streaming serial data to a figure where it is continously updated.
For the FuncAnimation to work you have a Figure, Func (function to call at each frame), and Frames (Source of data to pass func and each frame of the animation) fairly simple I thought and it does animate the way I would expect.
However the figure continues to chew up memory until it crashes the Raspberry Pi even though I have the blit flag set to optimize the drawing and only render the new data points.
Here is the code:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Setup plotting, one figure and two subplots
fig, (ax1, ax2) = plt.subplots(2,1)#,sharex=True)
g_line = ax1.plot([],[],'g')
r_line = ax1.plot([],[],'r')
lines = []
# Add each line to o ur tuples of lines to be plotted
lines.extend(g_line)
lines.extend(r_line)
# Data plotting function, this is call from the animation
# Each data element is in the data list being returned below
def plot_data(data):
x1 = data[0]; y1 = data[1];
x2 = data[2]; y2 = data[3];
lines[0].set_data(x1,y1) # Audio - green
lines[1].set_data(x2,y2) # Audio thresh - red
return tuple(lines)
def data_gen():
my_list = []
my_list2 = []
# Main infinite loop for processing of serial data
while True:
# Grab input RX serial data
(my_list, my_list2) = readUART(serReader)
yield my_list, my_list2
#end while
ani = animation.FuncAnimation(fig, plot_data, data_gen, interval=1, blit=True)
plt.show()
I have tried to clear the figure after each iteration but that severly slows down the rendering.
Is there some memory i'm not aware of that is holding onto it each time it plots even when the plotting fills the figure completely and moves off the screen?
Note - I ran a memory profiler on the entire program minus the plotting and just did it on the while TRUE loop and the memory didn't move past 55mb the entire time I let it run.