By Viviane Kakerbeck
Compiler: weakish
Editor’s note: Matplotlib has a lesser-known feature called animation.funcanimation, which allows you to create giFs from animation functions you write. Viviane Kakerbeck demonstrates the use of this feature with an example, and offers tips on how to make giFs look better by enhancing data and Gaussian smoothing.
Python’s Matplotlib and Seaborn are very useful drawing libraries. However, they create static images, and it is difficult to describe changes in data values in a dynamic and beautiful way. How great would it be if your next presentation, or your next blog post, showed the evolution of the data in motion graphics? Even better, you can continue to use Matplotlib, Seaborn, or whatever library you like.
I recently made some motion graphics for a documentary about the opioid crisis in the United States, so I will use relevant data in this article. Data from the U.S. national institute on drug abuse and the CDC’s public data, can be downloaded from the following url: www.drugabuse.gov/sites/defau…
In this article, we will use Matplotlib and Seaborn to draw graphs, and numpy and PANDAS to process the data. Matplotlib provides several functions that you can use to animate. Without further ado, let’s get started. First, introduce all dependencies.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
Copy the code
We then load the data and convert it to pandas’ DataFrame. I also wrote a helper function to load data from rows of interest for later drawing.
overdoses = pd.read_excel('overdose_data_1999-2015.xls',sheetname='Online',skiprows =6)
def get_data(table,rownum,title):
data = pd.DataFrame(table.loc[rownum][2:]).astype(float)
data.columns = {title}
return data
Copy the code
With that in mind, here is the main part of this article, how to animate.
First, if you, like me, use jupyter Notebook, I recommend using the %matplotlib notebook directive. This way, you can view the animation directly in the notebook without waiting to save it.
I used a helper function I wrote earlier, get_data, to get the number of heroin overdoses and packed it into a two-column panda DataFrame with one column for the year and one column for the number of overdoses.
%matplotlib notebook
title = 'Heroin Overdoses'
d = get_data(overdoses,18,title)
x = np.array(d.index)
y = np.array(d['Heroin Overdoses'])
overdose = pd.DataFrame(y,x)
#XN,YN = augment(x,y,10)
#augmented = pd.DataFrame(YN,XN)
overdose.columns = {title}
Copy the code
Next we initialize a writer that records 20fps (bit rate 1800) based on FFMPEG. Of course, you can adjust these parameters as needed.
Writer = animation.writers['ffmpeg']
writer = Writer(fps=20, metadata=dict(artist='Me'), bitrate=1800)
Copy the code
Next, create a labeled graph. Don’t forget to limit the range of x and y axes so that the animation doesn’t jump when displaying data.
FIG = plt.figure(figsize=(10,6)) plt.xlim(1999, 2016) plt.ylim(np.min(overdose)[0], np.max(overdose)[0]) plt.xlabel('Year',fontsize=20) plt.ylabel(title,fontsize=20) plt.title('Heroin Overdoses per Year',fontsize=20)Copy the code
The key to animation is to define an animation function that specifies what happens in each frame of the video. Here I represents the index of the animation frame. You can select the range of data visible in the I frame. I then use seaborn’s line graph to draw the selected data. I resized the last two lines to make the graphics look nicer.
def animate(i): Iloc [:int(I +1)] # P = sns.lineplot(x=data.index, y=data[title], data=data, color="r") p.tick_params(labelsize=17) plt.setp(p.lines,linewidth=7)Copy the code
After defined the animation function, using matplotlib. Animation. FuncAnimation definition animation should include how many frames, that is, through frames parameter definition to invoke the animate (I) frequency.
ani = matplotlib.animation.FuncAnimation(fig, animate, frames=17, repeat=True)
Copy the code
Then simply call ani.save() to save the animation as an MP4 file. If you want to see the effect before saving, use plt.show().
ani.save('HeroinOverdosesJumpy.mp4', writer=writer)
Copy the code
All right, let’s see what happens.
It looks ok, but it feels a little shaky. To alleviate jitter, we can insert some intermediate values into the existing data to smooth it out. To do this, we define the following data enhancement functions:
def augment(xold,yold,numsteps):
xnew = []
ynew = []
for i in range(len(xold)-1):
difX = xold[i+1]-xold[i]
stepsX = difX/numsteps
difY = yold[i+1]-yold[i]
stepsY = difY/numsteps
for s in range(numsteps):
xnew = np.append(xnew,xold[i]+s*stepsX)
ynew = np.append(ynew,yold[i]+s*stepsY)
return xnew,ynew
Copy the code
Then we need to apply this enhancement function on data, and accordingly increase the matplotlib. Animation. FuncAnimation function counts the number of frames. Here I call augment function with the parameter numsteps=10, that is, I increase the number of data points to 160 and set the number of frames to 160 accordingly. After enhancing the data, the result looks much smoother, but there are still some sharp corners in the curve where the data values change and it doesn’t look particularly nice.
To make the image more aesthetically pleasing, we implement a Gaussian smoothing function:
def smoothListGaussian(listin,strippedXs=False,degree=5): Window =degree*2-1 weight=np.array([1.0]*window) weightGauss=[] for I in range(window): i=i-degree+1 frac=i/float(window) gauss=1/(np.exp((4*(frac))**2)) weightGauss.append(gauss) Weight =np.array(weightGauss)*weight smoothed=[0.0]*(Len (listin)-window) for I in range(Len (smoothed)): smoothed[i]=sum(np.array(listin[i:i+window])*weight)/sum(weight) return smootheCopy the code
I won’t go into the details of Gaussian smoothing here, but interested readers can check out this article: www.swharden.com/wp/2008-11-…
Finally, we tweaked the color and style slightly to get the motion picture at the beginning of the article:
sns.set(rc={'axes.facecolor':'lightgrey', 'figure.facecolor':'lightgrey','figure.edgecolor':'black','axes.grid':False})
Copy the code
This article demonstrates the use of the matplotlib animation function with an example. Of course, you can use it for any graphics you want to animate. The possibilities are endless simply by adjusting the parameters and graphic types in the animate() function.
I hope you enjoy the whole feature of Matplotlib and make good use of it. Also, if you’re interested in the documentary I mentioned earlier, you can check it out on YouTube: Youtu.be /7xrvuSDLHiY