This notebook shows animations made in the style covered here. Learned of from this Stack0verflow post seeking help.
Important benefit of this approach: These animations are interactive and playable in static renderings of notebooks such as those displayed via nbviewer. (GitHub's viewer currently doesn't support this; you must use nbviewer.) No active kernel is needed once the notebook as been run once and saved.
This can be run in sessions launched from my animated_matplotlib-binder repo here.
It can be obtained by running the following in a notebook cell the launched session:
!curl -OL https://gist.githubusercontent.com/fomightez/d862333d8eefb94a74a79022840680b1/raw/8fd2185dd4d6d1c8aa78a02a061a59546898f19f/Creating%2520animated%2520plot%2520from%2520frames%2520that%2520displays%2520with%2520control%2520widget%2520in%2520Jupyter.ipynb
Following that, run the notebook from the Jupyter Dashboard or the file navigation pane, depending if you are using the classic notebook interface or JupyterLab, respectively.
This runs the example that was sought to display, although it has been altered to produce the animation and not an HTML5 movie, like the original post. If you are anyone except Wayne, you probably seek this one. (I, Wayne like the idea of leaving a read trail where the animation has covered and so kept the example below even though it isn't 'correct' because hard to see what point that one is at when scrubbing in reverse.)
Here the red dot makes it easy to follow from where on the plot of the curve at the bottom the upper values are being derived. You can easily 'scrub' back and forth. This is much more full-featured for complex comparative plots like this. The ability to scrub can also be very useful when wanting to discuss particular points in the plot. The examples at the main page that currently launch from my animated_matplotlib-binder play through once triggered and you cannot pause.
import scipy.integrate
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
plt.rcParams["animation.html"] = "jshtml"
plt.rcParams['figure.dpi'] = 150
plt.ioff()
def showConvolution(t0,f1, f2):
# Calculate the overall convolution result using Simpson integration
convolution = np.zeros(len(t))
for n, t_ in enumerate(t):
prod = lambda tau: f1(tau) * f2(t_-tau)
convolution[n] = scipy.integrate.simps(prod(t), t)
# Create the shifted and flipped function
f_shift = lambda t: f2(t0-t)
prod = lambda tau: f1(tau) * f2(t0-tau)
# Plot the curves
axes[0].clear() # il
axes[1].clear()
axes[0].set_xlim(-5, 5)
axes[0].set_ylim(0, 1.0)
#axes[0].set_ymargin(0.05) # il
axes[0].plot(t, f1(t), label=r'$f_1(\tau)$')
axes[0].plot(t, f_shift(t), label=r'$f_2(t_0-\tau)$')
#axes[0].fill(t, prod(t), color='r', alpha=0.5, edgecolor='black', hatch='//') # il
axes[0].plot(t, prod(t), 'r-', label=r'$f_1(\tau)f_2(t_0-\tau)$')
#axes[0].grid(True); axes[0].set_xlabel(r'$\tau$'); axes[0].set_ylabel(r'$x(\tau)$') # il
#axes[0].legend(fontsize=10) # il
#axes[0].text(-4, 0.6, '$t_0=%.2f$' % t0, bbox=dict(fc='white')) # il
# plot the convolution curve
axes[1].set_xlim(-5, 5)
axes[1].set_ylim(0, 0.4)
#axes[1].set_ymargin(0.05) # il
axes[1].plot(t, convolution, label='$(f_1*f_2)(t)$')
# recalculate the value of the convolution integral at the current time-shift t0
current_value = scipy.integrate.simps(prod(t), t)
axes[1].plot(t0, current_value, 'ro') # plot the point
#axes[1].grid(True); axes[1].set_xlabel('$t$'); axes[1].set_ylabel('$(f_1*f_2)(t)$') # il
#axes[1].legend(fontsize=10) # il
#plt.show() # il
Fs = 50 # our sampling frequency for the plotting
T = 5 # the time range we are interested in
t = np.arange(-T, T, 1/Fs) # the time samples
f1 = lambda t: np.maximum(0, 1-abs(t))
f2 = lambda t: (t>0) * np.exp(-2*t)
t0 = np.arange(-2.0,2.0, 0.05)
fig = plt.figure(figsize=(8,3))
axes= fig.subplots(2, 1)
anim = animation.FuncAnimation(fig, showConvolution, frames=t0, fargs=(f1,f2),interval=80)
anim
See the post for the rest of the code at the bottom:
#anim.save('animation.mp4', fps=30) # fps = frames per second
#plt.show()
from IPython.display import HTML
plt.close()
HTML(anim.to_html5_video())
This makes a video of the animation. I presume that animation video is what is included at the bottom of the post.
I presume the anim.save
line if uncommented produces a file that can be used by embeddable web-based players or played one's local machine. However, ffmpeg is not installed in the recommended session and so I have not fully tried. Running the original code this is adapted from in sessions here (assuming from the recommended launch) results in RuntimeError: Requested MovieWriter (ffmpeg) not available
. The last three lines of that current code can be deleted and then the animation will be shown when run in a fresh session with no control widget.
(This actually came first and separate from the example above. I liked that it produces a widget. As well as the effect of highlighting all of what has run.)
Notes on running and details from when working out to answer https://stackoverflow.com/q/70761535/8508004:
Slide the blue slider below the two sub-lots and above the control buttons after the plots and interface are generated. Generation takes like fifteen seconds; be patient.
I'm putting it here primarily as an example of ability to use slider controller to progress through. Adapted from a version from here. I think it's an interesting approach to use rendering the current point along the curve as red sphere to illustrate the point it has reached on the curve as progresses along the x axis (t, in this cases). (The changing of the color of the bottom plot, and slight movement of the y-axis label, I think is an artifact, or maybe it is comes from the line plt.plot(t, convolution, label='$(f_1*f_2)(t)$')
not specifying a color. It adds a neat vibrant effect.) I don't quite get the math; however, I think it is meant to show something similar to the gif here where illustrates integral for differant span as it progresses with a window along a curve. (Also note this is a very different approach than plots earlier because here curve(s) show the whole time and not adding the additional points a x-axis progresses, however, would seem only way to get the animation like in the gif here. It could be implemented in the simple approach lime up top by calulating entire curve for each moment and then use index to get full curve and not just a point as enumerare x.)
I, Wayne, like this idea of the red region showing what has already been covered, and so that is the only reason I kept this. I feel like that trick could be useful in a plot animation somewhere.
%matplotlib notebook
import scipy.integrate
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
plt.rcParams["animation.html"] = "jshtml"
plt.rcParams['figure.dpi'] = 150
plt.ioff()
def showConvolution(t0, f1, f2):
# Calculate the overall convolution result using Simpson integration
convolution = np.zeros(len(t))
for n, t_ in enumerate(t):
prod = lambda tau: f1(tau) * f2(t_-tau)
convolution[n] = scipy.integrate.simps(prod(t), t)
# Create the shifted and flipped function
f_shift = lambda t: f2(t0-t)
prod = lambda tau: f1(tau) * f2(t0-tau)
# Plot the curves
plt.subplot(211)
plt.cla()
plt.plot(t, f1(t), label=r'$f_1(\tau)$')
plt.plot(t, f_shift(t), label=r'$f_2(t_0-\tau)$')
plt.plot(t, prod(t), 'r-', label=r'$f_1(\tau)f_2(t_0-\tau)$')
# plot the convolution curve
plt.subplot(212)
plt.plot(t, convolution, label='$(f_1*f_2)(t)$')
# recalculate the value of the convolution integral at the current time-shift t0
current_value = scipy.integrate.simps(prod(t), t)
plt.plot(t0, current_value, 'ro') # plot the point
Fs = 50 # our sampling frequency for the plotting
T = 5 # the time range we are interested in
t = np.arange(-T, T, 1/Fs) # the time samples
f1 = lambda t: np.maximum(0, 1-abs(t))
f2 = lambda t: (t>0) * np.exp(-2*t)
t0 = np.arange(-2.0,2.0, 0.05)
fig = plt.figure(figsize=(8,3))
anim = animation.FuncAnimation(fig, showConvolution, frames=t0, fargs=(f1,f2),interval=80)
anim
The one above is 'special' to leave on highlighting of the region.
However, the main point of the highlighting is meant to highlight the current point where the derivative is being calculated. The one at the top of this notebook shows the actual highlight of the point that was more the purpose of the highlight in this case. (I just liked the trailing, region highlighting look although it makes it hard to interpret what is supposed to be read when reversing because no longer really showing what just has been run, which was the point of that highlighted region. The above section makes it look like how it should.)