repeat_delay parameter has no effect in animated plot in Jupyter - python

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?

Related

Distorted tick labels with matplotlib.animation

With more recent versions of matplotlib I'm getting distorted labels on animated plots. Is anybody else getting this? Am I doing something wrong?
Minimum working example:
from netCDF4 import Dataset
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython import display
print(matplotlib.__version__)
# Make a random array of data to plot
r = np.random.rand(10,100,100)
fig, ax = plt.subplots(1,1,figsize=(10,10))
# Initiate Plot
plot = ax.pcolormesh(range(0,100),
range(0,100),
r[0],
cmap='RdBu')
cb = fig.colorbar(plot)
cb.set_label('Temperature')
def animate(frame):
C = r[frame][:-1,:-1]
plot.set_array(C)
ani = animation.FuncAnimation(fig,
animate,
frames= range(0,r.shape[0]),
)
video = ani.to_html5_video()
html = display.HTML(video)
display.display(html)
plt.close()
My labels seem like they're being placed many times (although this bug persists even when the animation only has two frames), and they appear like this:
This behaviour does not occur if I comment out all the lines below the ax.pcolormesh... which creates static plot that looks fine.
I'm on matplotlib 3.3.2

Matplotlib FuncAnimation frame not rendering when a new artist added

I am making an animation in Matplotlib where new artists (specifically patches) are added every few frames, but when I run it, every frame in which a new artist is added is completely blank. I know there is some issue with the blitting since it works when I turn that off, but I need it on. I return every shape that is created or modified in each frame, just like the documentation requires. I am using the MacOSX backend.
My code looks similar to this:
from random import random
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
axe = fig.add_axes([0, 0, 1, 1], frameon=False)
circles = []
def update(i):
if not i % 10:
new_circle = plt.Circle((random(), random()), 0.05, color='black')
axe.add_patch(new_circle)
circles.append(new_circle)
for circle in circles:
circle.center = (random(), random())
return circles
animation = FuncAnimation(fig, update, frames=60, interval=1000/30, repeat=False, blit=True)
plt.show()
This appears to be a bug with matplotlib in the MacOSX backend, so the solution is just to work around it by using a different backend or not blitting if possible.

Redrawing Seaborn Figures for Animations

Some seaborn methods like JointPlot create new figures on each call. This makes it impossible to create a simple animation like in matplotlib where iterative calls to plt.cla() or plt.clf() allow to update the contents of a figure without closing/opening the window each time.
The only solution I currently see is:
for t in range(iterations):
# .. update your data ..
if 'jp' in locals():
plt.close(jp.fig)
jp = sns.jointplot(x=data[0], y=data[1])
plt.pause(0.01)
This works because we close the previous window right before creating a new one. But of course, this is far from ideal.
Is there a better way? Can the plot somehow be done directly on a previously generated Figure object? Or is there a way to prevent these methods to generate new figures on each call?
sns.jointplot creates a figure by itself. In order to animate the jointplot, one might therefore reuse this created figure instead of recreating a new one in each iteration.
jointplot internally creates a JointGrid, so it makes sense to directly use this and plot the joint axes and the marginals individually. In each step of the animation one would then update the data, clear the axes and set them up just as during creation of the grid. Unfortunately, this last step involves a lot of code lines.
The final code may then look like:
import matplotlib.pyplot as plt
import matplotlib.animation
import seaborn as sns
import numpy as np
def get_data(i=0):
x,y = np.random.normal(loc=i,scale=3,size=(2, 260))
return x,y
x,y = get_data()
g = sns.JointGrid(x=x, y=y, size=4)
lim = (-10,10)
def prep_axes(g, xlim, ylim):
g.ax_joint.clear()
g.ax_joint.set_xlim(xlim)
g.ax_joint.set_ylim(ylim)
g.ax_marg_x.clear()
g.ax_marg_x.set_xlim(xlim)
g.ax_marg_y.clear()
g.ax_marg_y.set_ylim(ylim)
plt.setp(g.ax_marg_x.get_xticklabels(), visible=False)
plt.setp(g.ax_marg_y.get_yticklabels(), visible=False)
plt.setp(g.ax_marg_x.yaxis.get_majorticklines(), visible=False)
plt.setp(g.ax_marg_x.yaxis.get_minorticklines(), visible=False)
plt.setp(g.ax_marg_y.xaxis.get_majorticklines(), visible=False)
plt.setp(g.ax_marg_y.xaxis.get_minorticklines(), visible=False)
plt.setp(g.ax_marg_x.get_yticklabels(), visible=False)
plt.setp(g.ax_marg_y.get_xticklabels(), visible=False)
def animate(i):
g.x, g.y = get_data(i)
prep_axes(g, lim, lim)
g.plot_joint(sns.kdeplot, cmap="Purples_d")
g.plot_marginals(sns.kdeplot, color="m", shade=True)
frames=np.sin(np.linspace(0,2*np.pi,17))*5
ani = matplotlib.animation.FuncAnimation(g.fig, animate, frames=frames, repeat=True)
plt.show()
using the celluloid package (https://github.com/jwkvam/celluloid) I was able to animate seaborn plots without much hassle:
import numpy as np
from celluloid import Camera
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
fig = plt.figure()
camera = Camera(fig)
# animation draws one data point at a time
for i in range(0, data.shape[0]):
plot = sns.scatterplot(x=data.x[:i], y=data.y[:i])
camera.snap()
anim = camera.animate(blit=False)
anim.save('animation.mp4')
I'm sure similar code could be written for jointplots

Updating pyplot graph in real time

I'm trying to plot a 2D grid of data and map them to colors. Then I want to update the values and have the graph update with the new values. Currently the graph only shows the final result, not all the middle phases the graph should go through.
MY CODE::
import matplotlib.pyplot as pyplot
import matplotlib as mpl
import numpy as np
import time
import matplotlib.animation as animation
thing=0
NUM_COL=10
NUM_ROW=10
zvals=np.full((NUM_ROW,NUM_COL),-5.0)
def update_graph(zvals):
zvals+=1
pyplot.clf()
img = pyplot.imshow(zvals,interpolation='nearest',
cmap = cmap,norm=norm)
time.sleep(1)
pyplot.draw()
# make a color map of fixed colors
cmap = mpl.colors.ListedColormap(['blue','black','red'])
bounds=[-6,-2,2,6]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
# tell imshow about color map so that only set colors are used
img = pyplot.imshow(zvals,interpolation='nearest',
cmap = cmap,norm=norm)
# make a color bar
pyplot.colorbar(img,cmap=cmap,norm=norm,boundaries=bounds,ticks=[-5,0,5])
pyplot.draw()
for i in range(5):
update_graph(zvals)
pyplot.show()
pyplot does not generally show anything until pyplot.show() is called, unless matplotlib runs in 'interactive' mode. The interactive mode is entered by calling pyplot.ion() and can exited again by calling pyplot.ioff().
Thus it should be possible for you to see all your updates by calling pyplot.ion() somewhere before doing anything you want to be directly updated and then end your program with pyplot.ioff() to get back to the standard pyplot way when done.
However, it may not look very smooth, depending on your system and what updates you are doing.
So I'm not sure if this a good answer or not, I have only used updating plots once before. But this is a way to achieve what you want.
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
NUM_COL = 10
NUM_ROW = 10
zvals = np.full((NUM_ROW,NUM_COL),-5.0)
cmap = mpl.colors.ListedColormap(['blue','black','red'])
bounds = [-6,-2,2,6]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig = plt.figure() # Create the figure
img = plt.imshow(zvals,interpolation='nearest', cmap=cmap,norm=norm) # display the first image
plt.colorbar(img,cmap=cmap,norm=norm,boundaries=bounds,ticks=[-5,0,5]) # create your colour bar
# If we dont have this, then animation.FuncAnimation will call update_graph upon initialization
def init():
pass
# animation.FuncAnimation will use this function to update the plot. This is where we update what we want displayed
def update_graph(frame):
global zvals # zvals is a global variable
zvals+=1
img.set_data(zvals) # This sets the data to the new, updated values
print("Frame Update {}".format(frame)) # this is for debugging to help you see whats going on
return img
# This is what will run the animations
anim = animation.FuncAnimation(fig, update_graph, init_func = init,
interval = 1000, # update every 1000ms
frames = 8, # Update 8 times
repeat=False) # After 8 times, don't repeat the animation
plt.show() # show our plot

Pausing a matplotlib ArtistAnimation

As per the title, I'm wondering if it is possible to pause a matplotlib ArtistAnimation. I know it is possible to pause when using FuncAnimation, but I am not sure that that method can be applied to an ArtistAnimation.
An example of a working ArtistAnimation without pausing is
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
import numpy as np
fig, ax = plt.subplots()
ax.set(xlim=(0, 2*np.pi), ylim=(-1, 1))
x = np.linspace(0, 2*np.pi, 100)
ims = [] # Blank list that will contain all frames
for frame in range(50):
line, = ax.plot(x, np.sin(x + 0.1*frame), color='k')
# Add new element to list with everything that changes between frames
ims.append([line])
anim = ArtistAnimation(fig, ims, interval=100)
The following is not a complete solution, but maybe some way toward one. It requires IPython be used.
Using anim as defined in the question, I can enter anim._stop() to pause the animation. I can also use anim._step() as needed to see the next frames.
I'm not sure if it's possible to get the animation to start running again after these calls.

Categories

Resources