[Tut] Matplotlib Animation – A Helpful Illustrated Guide - Printable Version +- Sick Gaming (https://www.sickgaming.net) +-- Forum: Programming (https://www.sickgaming.net/forum-76.html) +--- Forum: Python (https://www.sickgaming.net/forum-83.html) +--- Thread: [Tut] Matplotlib Animation – A Helpful Illustrated Guide (/thread-95038.html) |
[Tut] Matplotlib Animation – A Helpful Illustrated Guide - xSicKxBot - 05-13-2020 Matplotlib Animation – A Helpful Illustrated Guide <div><figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio"> <div class="wp-block-embed__wrapper"> <div class="ast-oembed-container"><iframe title="Matplotlib Animation – A Helpful Illustrated Guide" width="1333" height="1000" src="https://www.youtube.com/embed/F57_0XPdhD8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div> </div> </figure> <p>Creating animations in matplotlib is reasonably straightforward. However, it can be tricky when starting, and there is no consensus for the best way to create them. In this article, I show you a few methods you can use to make amazing animations in matplotlib.</p> <h2 id="Matplotlib-Animation-Example">Matplotlib Animation Example</h2> <p>The hardest thing about creating animations in matplotlib is coming up with the idea for them. This article covers the basic ideas for line plots, and I may cover other plots such as scatter and 3D plots in the future. Once you understand these overarching principles, you can animate other plots effortlessly.</p> <p>There are two classes you can use to create animations: <code>FuncAnimation</code> and <code>ArtistAnimation.</code> I focus on <code>FuncAnimation</code> as this is the more intuitive and more widely used one of the two.</p> <p>To use <code>FuncAnimation,</code> define a function (often called <code>animate</code>), which matplotlib repeatedly calls to create the next frame/image for your animation.</p> <p>To create an animation with <code>FuncAnimation</code> in matplotlib, follow these seven steps:</p> <ol> <li>Have a clear picture in your mind of what you want the animation to do</li> <li>Import standard modules and <code>FuncAnimation</code></li> <li>Set up <code>Figure</code>, <code>Axes</code>, and <code>Line</code> objects</li> <li>Initialize data</li> <li>Define your animation function – <code>animate(i)</code></li> <li>Pass everything to <code>FuncAnimation</code></li> <li>Display or save your animation</li> </ol> <p>Let’s create a sin wave that matplotlib ‘draws’ for us. Note that this code may look strange to you when you first read it. Creating animations with matplotlib is different from creating static plots. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Standard imports import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation </pre> <p>Import <a href="https://blog.finxter.com/numpy-tutorial/" target="_blank" rel="noreferrer noopener">NumPy </a>and matplotlib using their standard aliases and <code>FuncAnimation</code> from <code>matplotlib.animation</code>.</p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Set up empty Figure, Axes and Line objects fig, ax = plt.subplots() # Set axes limits so that the whole image is included ax.set(xlim=(-0.1, 2*np.pi+0.1), ylim=(-1.1, 1.1)) # Draw a blank line line, = ax.plot([], []) </pre> <figure class="wp-block-image size-large"><img src="https://blog.finxter.com/wp-content/uploads/2020/05/image-56.png" alt="" class="wp-image-8437" width="650" srcset="https://blog.finxter.com/wp-content/uploads/2020/05/image-56.png 386w, https://blog.finxter.com/wp-content/uploads/2020/05/image-56-300x195.png 300w" sizes="(max-width: 386px) 100vw, 386px" /></figure> <p>Set up the <code>Figure</code> and <code>Axes</code> objects using <a href="https://blog.finxter.com/matplotlib-subplots/"><code>plt.subplots()</code></a> and – using <code>ax.set()</code> – set the x- and y-axis limits to the same size as a normal sine curve – from 0 to 2π on the x-axis and from -1 to 1 on the y-axis. Note that I included padding of 0.1 on each axis limit so that you can see the whole line matplotlib draws.</p> <p>Then, I did something you have probably never done before: I drew a blank line. You need to do this because <code>animate</code> modifies this line, and it can only modify something that already exists. You can also think of it as initializing an empty line object that you will soon fill with data.</p> <p>Note that you must include a comma <em>after</em> <code>line,</code>! The plot method returns a tuple, and you need to unpack it to create the variable <code>line</code>. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Define data - one sine wave x = np.linspace(0, 2*np.pi, num=50) y = np.sin(x) </pre> <p>Next, define the data you want to plot. Here, I am plotting one sine wave, so I used <a href="https://blog.finxter.com/np-linspace/"><code>np.linspace()</code></a> to create the x-axis data and created <code>y</code> by calling <code>np.sin()</code> on <code>x</code>. Thanks to <a href="https://blog.finxter.com/numpy-broadcasting-a-simple-tutorial/" target="_blank" rel="noreferrer noopener">numpy broadcasting</a>, it is easy to apply functions to NumPy arrays! </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Define animate function def animate(i): line.set_data(x[:i], y[:i]) return line, </pre> <p>Define the <code>animate(i)</code> function. Its argument <code>i</code> is an integer starting from 0 and up to the total number of frames you want in your animation. I used the <code>line.set_data()</code> method to draw the first <code>i</code> elements of the sine curve for both <code>x</code> and <code>y</code>. Note that you return <code>line,</code> with a comma again because you need to return <em>an iterable</em> and adding a comma makes it a tuple. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Pass to FuncAnimation anim = FuncAnimation(fig, animate, frames=len(x)+1, interval=30, blit=True) </pre> <p>Create a <code>FuncAnimation</code> object. First, pass the <code>Figure</code> and <code>animate</code> function as positional arguments.</p> <p>Next, set the number of <code>frames</code> to <code>len(x)+1</code> so that it includes all the values in <code>x</code>. It works like the <code>range()</code> function, and so even though <code>x</code> has length 50, python only draws frames 0 to 49 and not the 50th member. So, add on one more to draw the entire plot.</p> <p>The <code>interval</code> is how long in milliseconds matplotlib waits between drawing the next part of the animation. I’ve found that 30 works well as a general go-to. A larger number means matplotlib waits longer between drawing and so the animation is slower.</p> <p>Finally, set <code>blit=True</code> so that it only redraws parts that have not been drawn before. It doesn’t make much difference for this example, but once you create more complex plots, you should use this; it can take quite a while for matplotlib to create animations (waiting several minutes is common for large ones). </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Save in the current working directory anim.save('sin.mp4') </pre> <p>I saved the animation as a video called ‘sin.mp4’ to the current working directory.</p> <figure class="wp-block-video"><video controls="" src="https://blog.finxter.com/wp-content/uploads/2020/05/sin-2.mp4" width="650"></video></figure> <p>Open your current working directory, find the saved video, and play it. Congratulations! You’ve just made your first animation in matplotlib!</p> <p>Some of the steps you take to create animations are unique and may feel unusual the first time you try them. I know I felt strange the first time I used them. Don’t worry, the more you practice and experiment, the easier it becomes.</p> <p>Here’s the full code:</p> <pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Standard imports import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation # Set up empty Figure, Axes and Line objects fig, ax = plt.subplots() # Set axes limits so that the whole image is included ax.set(xlim=(-0.1, 2*np.pi+0.1), ylim=(-1.1, 1.1)) # Draw a blank line line, = ax.plot([], []) # Define data - one sine wave x = np.linspace(0, 2*np.pi, num=50) y = np.sin(x) # Define animate function def animate(i): line.set_data(x[:i], y[:i]) return line, # Pass to FuncAnimation anim = FuncAnimation(fig, animate, frames=len(x)+1, interval=30, blit=True) # Save in the current working directory anim.save('sin.mp4')</pre> <p>Note that if this final step did not work for you, it’s probably because you don’t have the right libraries installed – I’ll show you what to install right now.</p> <p><strong>Related: </strong></p> <ul> <li><a href="https://blog.finxter.com/matplotlib-3d-plot-full/">Matplotlib 3D Plot – Full Tutorial</a></li> <li><a rel="noreferrer noopener" href="https://blog.finxter.com/matplotlib-boxplot/" target="_blank">Matplotlib Boxplot – A Helpful Illustrated Guide</a></li> <li><a rel="noreferrer noopener" href="https://blog.finxter.com/matplotlib-legend/" target="_blank">Matplotlib Legend</a></li> <li><a rel="noreferrer noopener" href="https://blog.finxter.com/matplotlib-imshow/" target="_blank">Matplotlib Imshow</a></li> <li><a rel="noreferrer noopener" href="https://blog.finxter.com/matplotlib-subplot/" target="_blank">Matplotlib Subplot </a>and <a rel="noreferrer noopener" href="https://blog.finxter.com/matplotlib-subplots/" target="_blank">Subplots</a></li> <li><a rel="noreferrer noopener" href="https://blog.finxter.com/matplotlib-line-plot/" target="_blank">Matplotlib Lineplot</a></li> <li><a href="https://blog.finxter.com/matplotlib-scatter-plot/">Matplotlib Scatter Plot</a></li> <li><a href="https://blog.finxter.com/matplotlib-histogram/" target="_blank" rel="noreferrer noopener">Matplotlib Histogram</a></li> </ul> <h2 id="Matplotlib-Animation-Save">Matplotlib Animation Save</h2> <p>To save your animation in matplotlib, use the <code>.save()</code> method on your <code>FuncAnimation</code> object. You can either save them as mp4 videos or gifs.</p> <h2 id="Matplotlib-Animation-Save-Mp4">Matplotlib Animation Save Mp4</h2> <p>To save animations as mp4 videos, first, install the FFmpeg library. It’s an incredibly powerful command-line tool, and you can download it from their <a href="https://www.ffmpeg.org/">official site</a>, <a href="https://github.com/FFmpeg/FFmpeg">Github</a>, or, if you use <a href="https://www.anaconda.com/">anaconda</a>, by running <code>conda install ffmpeg</code>.</p> <p>Once you have created your animation, run <code>anim.save('name.mp4', writer='ffmpeg')</code>, and python saves your animation as the video ‘name.mp4’ in your current working directory.</p> <p>Note that the default writer is FFmpeg, and so you don’t have to explicitly state it if you don’t want to.</p> <h2 id="Matplotlib-Animation-Save-Gif">Matplotlib Animation Save Gif</h2> <p>To save animations as gifs, first, install the ImageMagick library. It is a command-line tool, and you can download it from their <a href="https://imagemagick.org/script/download.php">official site</a>, <a href="https://github.com/ImageMagick/ImageMagick">GitHub</a>, or if you use <a href="https://www.anaconda.com/">anaconda</a>, by running <code>conda install -c conda-forge imagemagick</code>.</p> <p>Once you have created your animation, run <code>anim.save('name.gif', writer='imagemagick')</code>, and python saves your animation as the gif ‘name.gif’ in your current working directory. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">anim.save('sin.gif', writer='imagemagick') </pre> <figure class="wp-block-image size-large"><img src="https://blog.finxter.com/wp-content/uploads/2020/05/sin.gif" alt="" class="wp-image-8444" width="650"></figure> <p>Note that both ImageMagick and FFmpeg are command-line tools and not python libraries. As such, you cannot install them using <code>pip</code>. There are some python wrappers for those tools online, but they are <em>not</em> what you need to install.</p> <h2 id="Matplotlib-Animation-Jupyter">Matplotlib Animation Jupyter</h2> <p>If you write your code in Jupyter notebooks (something I highly recommend), and don’t want to save your animations to disk every time, you may be disappointed to hear that your animations do not work out of the box. By default, Jupyter renders plots as static png images that <a href="https://stackoverflow.com/questions/43445103/inline-animations-in-jupyter">cannot be animated</a>.</p> <p>To fix this, you have a couple of options to choose from:</p> <ol> <li>The <code>%matplotlib notebook</code> magic command, or</li> <li>Changing the <code>plt.rcParams</code> dictionary</li> </ol> <p>If you run <code>%matplotlib notebook</code> in a code cell at the top of your notebook, then all your plots render in interactive windows. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Run at the top of your notebook %matplotlib notebook </pre> <figure class="wp-block-video"><video controls="" src="https://blog.finxter.com/wp-content/uploads/2020/05/notebook_option.mov" width="650"></video></figure> <p>As I cannot show you interactive Jupyter windows in a blog post, the above video shows you the result of running the sin drawing curve above.</p> <p>This method is by far the simplest but gives you the least control. For one thing, the buttons at the bottom do nothing. Plus, it keeps running until you click the off button at the top… but when you do, you have no way to turn it on again!</p> <p>I much prefer using the default <code>%matplotlin inline</code> style for all my plots, but you are free to choose whichever you want.</p> <p>The other option is to change the <code>plt.rcParams</code> dictionary. This dictionary controls the default behavior for all your matplotlib plots such as figure size, font size, and how your animations should display when you call them.</p> <p>If you print it to the screen, you can see all the parameters it controls.</p> <p>The one you are interested in is <code>animation.html</code>, which is <code>none</code> by default. The other options are: <code>'html5'</code> and <code>'jshtml'</code>.</p> <p>Let’s see what happens when you set <code>plt.rcParams</code> to those options. First, let’s look at <code>'html5'</code>.</p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">plt.rcParams['animation.html'] = 'html5' anim</pre> <p>Now the animation is rendered as an HTML5 video that plays as a loop. You can start/stop it using the play/pause buttons, but that’s about it. In my experience, this video plays much smoother than the interactive windows produced by <code>%matplotlib notebook</code>.</p> <p>Now let’s look at the much more powerful option: <code>'jshtml'</code> which stands for Javascript HTML. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">plt.rcParams['animation.html'] = 'jshtml' anim </pre> <p>Now your animations are displayed in interactive javascript windows! This option is by far the most powerful one available to you.</p> <p>Here are what each of the keys do (starting from the far left):</p> <ul> <li>speed up/slow down: + / –</li> <li>jump to end/start: |<< / >>|</li> <li>move one frame backwards/forwards: |< / >|</li> <li>play it backwards/forwards: < / ></li> <li>pause with the pause key</li> </ul> <p>Moreover, you can choose to play it ‘Once’, ‘Loop’ infinitely or play forwards and backward indefinitely using ‘Reflect’.</p> <p>To have all your animations render as either HTML5 video or in interactive javascript widgets, set <code>plt.rcParams</code> at the top of your code.</p> <h2 id="Conclusion">Conclusion</h2> <p>Excellent! You now know how to create basic animations in matplotlib. You know how to use <code>FuncAnimaton</code>, how to save them as videos or gifs, and plot animations in Jupyter notebooks.</p> <p>You’ve seen how to create ‘drawing’ plots, but there are many other things you can do, such as creating moving plots, animating 3D plots, and even scatter graphs. But we’ll leave them for another article.</p> <h2 id="Where-To-Go-From-Here?">Where To Go From Here?</h2> <p>Do you wish you could be a programmer full-time but don’t know how to start?</p> <p>Check out the pure value-packed webinar where Chris – creator of Finxter.com – teaches you to become a Python freelancer in 60 days or your money back!</p> <p><a href="https://tinyurl.com/become-a-python-freelancer">https://tinyurl.com/become-a-python-freelancer</a></p> <p>It doesn’t matter if you’re a Python novice or Python pro. If you are not making six figures/year with Python right now, you will learn something from this webinar.</p> <p>These are proven, no-BS methods that get you results fast.</p> <p>This webinar won’t be online forever. Click the link below before the seats fill up and learn how to become a Python freelancer, guaranteed.</p> <p><a href="https://tinyurl.com/become-a-python-freelancer">https://tinyurl.com/become-a-python-freelancer</a></p> </div> https://www.sickgaming.net/blog/2020/05/13/matplotlib-animation-a-helpful-illustrated-guide/ |