Matplotlib animation not appearing in Jupyter Notebook

This modification of your code below, altered at the start and end, will work in JupyterLab and Jupyter Notebook 7+ where you have installed ipympl:

%matplotlib ipympl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

stepsize = 0.5
num_steps = 20
num_trials = 5

final_position = []

for _ in range(num_trials):
    pos = np.array([0, 0])
    path = []
    for i in range(num_steps):
        pos = pos + np.random.normal(0, stepsize, 2)
        path.append(pos)
    final_position.append(np.array(path))
    
x = [final_position[i][:,0] for i in range(len(final_position))]
y = [final_position[j][:,1] for j in range(len(final_position))]

fig = plt.figure(figsize=(10,6))
ax = fig.add_subplot()
fig.subplots_adjust(left=0.1, right=0.85)

cmap = plt.get_cmap('tab10')

def animate(frame):
    step_num = frame % (num_steps)
    trial_num = frame//(num_steps)
    color = cmap(trial_num % 10)
    if step_num == num_steps-1:
        label = f"Trial = {trial_num+1}"
    else:
        label = None
    ax.plot(x[trial_num][:step_num], y[trial_num][:step_num], color = color, ls = '-',linewidth = 0.5,
            marker = 'o', ms = 8, mfc = color, mec ='k', zorder = trial_num, label = label)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_title(f"Number of trials = {trial_num+1} \nNumber of steps = {step_num+1}")  
    if step_num == num_steps-1:
        ax.legend(fontsize=10, loc='upper left', bbox_to_anchor=(1, 1))
    ax.grid(True)
    
    return ax

fig.suptitle(f"2D random walk simulation for {num_steps} steps over {num_trials} trials.")
ani = FuncAnimation(fig, animate, frames= np.arange(0, (num_steps * num_trials)), interval = 100, repeat = False)
ani;

You can try it in JupyterLab with an session served by MyBinder already set for it work by going here and clicking the ‘launch binder’ badge under ‘Direct links to start out in notebook mode in JupyterLab full-featured mode:’ (or click here directly to launch).

You can try it in Jupyter Notebook 7.1 by going here and clicking the ‘launch binder’ badge. This session won’t have ipympl already installed and so it will be a good example of the situation you may face. When the session comes up, use the ‘New’ button in the upper right to open a new notebook file and in the first cell run %pip install ipympl. Let it run successfully until it says you may restart the kernel. However, restarting the kernel won’t be enough as ipympl is a complex install and the settings need to be fully propagated to the rendering abilities of the browser. So I found if you just restart the kernel you’ll see a box that says:

Click to show javascript error

And if you click on that box, you’ll see something like this:

[Open Browser Console for more detailed log - Double click to close this message]
Failed to load model class 'MPLCanvasModel' from module 'jupyter-matplotlib'
Error: No version of module jupyter-matplotlib is registered
    at f.loadClass (https://notebooks.gesis.org/binder/jupyter/user/jtpio-83b0b625c-7f3d8714f98182e-uyw0i1a8/lab/extensions/@jupyter-widgets/jupyterlab-manager/static/134.a63a8d293fb35a52dc25.js?v=a63a8d293fb35a52dc25:1:74936)

Doing what I am going to step through will help you avoid that and get ipympl working in this session:

  1. After the ipympl installation step, replace the install command line with the plotting code from above.

  2. Restart the kernel using the ‘Kernel’ menu.

  3. Run the code and save the notebook. (You can just click the disk icon on the upper left side on the toolbar below the ‘File’ menu and it will as as ‘Untitled’ which is fine for now.) It won’t work and it will show the javascript error indicator. That’s okay because next we’ll propagate the changes with some steps. However, it is important to save the code in the notebook at this time to be ready for the next steps.

  4. Now that the plotting code is saved, close the notebook by clicking the ‘x’ in the upper right side of the tab that the notebook is currently open in.

  5. Back in the Jupyter Dashboard, which would have remained open while you had opened the new notebook, click on the ‘Running’ tab in the upper left below the ‘File’ menu bar and shut down all running kernels using the orange ‘Shut Down All’ text on the right upper side and then hit the Jupyter refresh symbol, which is a circular arrowed line pointing to its tail, in the upper right side just above where you shut down all kernels.

  6. Next, in the Jupyter Dashboard, click back on the ‘Files’ tab in the upper left below the ‘File’ menu bar, and next you want to do a hard refresh of your browser window. In Chrome on a Mac, you press Shift + command + r and it will reload the page with a hard refresh. (It may take a few seconds. Be patient.)

  7. Now double click on the saved notebook to reopen it.

  8. In that notebook window that just re-opened, restart the kernel again using the ‘Kernel’ menu.

  9. Run the code. You should see the animation running like you used to have with the older Juptyer Notebook interface.
    Having gotten all the changes propagated you won’t have to do all those steps again in the current session to get ipympl working and you should be able to re-run the cell and next see ‘Figure 2’ across the top, and then if you run the cell again ‘Figure 3’ and so forth.
    If you start a new notebook in this session, you can use the ipympl abilities without any of those complex steps, too. You can easily test that by opening a new notebook and copying in the plotting code we’ve been running.
    If you start a new session though you have to do that all again unless you make a fork of the gist where we launched from and add ipympl to the requirements.txt list. (I have resisted doing that as Jupyter Notebook 7+ is fairly actively being developed at this time, and so it is useful to use the versions released with MyBinder settings by jtpio.)

Optional version with a player controller widget that doesn’t need ipympl

This version if your code works in JupyterLab and Jupyter Notebook 7+ and doesn’t involve ipympl:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
plt.rcParams["animation.html"] = "jshtml"
#plt.ioff() #needed so the second time you run it you get only single plot

stepsize = 0.5
num_steps = 20
num_trials = 5

final_position = []

for _ in range(num_trials):
    pos = np.array([0, 0])
    path = []
    for i in range(num_steps):
        pos = pos + np.random.normal(0, stepsize, 2)
        path.append(pos)
    final_position.append(np.array(path))
    
x = [final_position[i][:,0] for i in range(len(final_position))]
y = [final_position[j][:,1] for j in range(len(final_position))]

fig = plt.figure(figsize=(10,6))
ax = fig.add_subplot()
fig.subplots_adjust(left=0.1, right=0.85)

cmap = plt.get_cmap('tab10')

def animate(frame):
    step_num = frame % (num_steps)
    trial_num = frame//(num_steps)
    color = cmap(trial_num % 10)
    if step_num == num_steps-1:
        label = f"Trial = {trial_num+1}"
    else:
        label = None
    ax.plot(x[trial_num][:step_num], y[trial_num][:step_num], color = color, ls = '-',linewidth = 0.5,
            marker = 'o', ms = 8, mfc = color, mec ='k', zorder = trial_num, label = label)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_title(f"Number of trials = {trial_num+1} \nNumber of steps = {step_num+1}")  
    if step_num == num_steps-1:
        ax.legend(fontsize=10, loc='upper left', bbox_to_anchor=(1, 1))
    ax.grid(True)
    
    return ax

fig.suptitle(f"2D random walk simulation for {num_steps} steps over {num_trials} trials.")
ani = FuncAnimation(fig, animate, frames= np.arange(0, (num_steps * num_trials)), interval = 100, repeat = False)
ani

You can try it in a JupyterLab session that comes up if you click here. (It also works in Jupyter Notebook 7+, and you can try in that if you click here).

Note though the animation result and interface is different than the ipympl one (the interactive one like in the older notebook interface). It runs the entire animation and makes frames and then displays them under control of a player controller widget that you have to run to play the animation. Importantly, that player controller widget approach will work even when the notebook is ‘static’ as you can see for an unrelated example here. I put static in quotes because some active features will work in nbviewer, though usually note in Github preview, which trips up a lot of novices now that Github attempts a preview rendering for notebooks even of substantial length. Also, approach at present is a little quirky in that it shows a final frame at the bottom. For a while that would go away upon replay; however, right now that doesn’t seem to be the case. You can though uncomment the line #plt.ioff() #needed so the second time you run it you get only single plot and when you play it the second time, you don’t get the final frame showing below the player controller widget.

You’ll see in that in the notebook that comes up in the JupyterLab session that I also have an animation approach that uses clear_output from from IPython.display import clear_output that is based on here. This is more of an active playing animation like the ipyml with FuncAnimation(); however,since yours already uses FuncAnimation() that is the more convenient route.

1 Like