How to create animations in Python

Today we are going to learn how to create animations in Python, to make our visualizations much more impressive and we can give more information in a visual and impressive way. In this post, you will learn how to create all kinds of animations in Python from simple animations to animated graphics like bar chart races. Sound interesting to you? Well, let’s get to it!

How animations work in Python

To create our animations we will use the FuncAnimation function inside matplolib. A fundamental aspect to be able to create our animations is to understand that this function does not create whole animations with interpolation, but simply limits itself to creating animations from a series of graphics that we pass to it.

This is very important, as it is a very different approach from other packages like R’s gganimate (if you don’t know how it works, here you have the tutorial).

In fact, to create animations in Python using FuncAnimation you simply have to pass a function that has as input value a number that refers to a frame and returns the graphic corresponding to that frame.

This means that to create animations in Python we must prepare the data very well. Let’s see how to do it.

Estructura de datos para crear animaciones en Python

Although you can create graphs from data with a very different shape, in my opinion, to make it easier to create the visualization the data must be in tidy format, that is:

  1. Each variable must be in a column.
  2. Each observation of that variable must be a different row.

Let’s see an example with the gapminder dataset, which is what we’ll use as an example:

from gapminder import gapminder
gapminder.head()
       country continent  year  lifeExp       pop   gdpPercap
0  Afghanistan      Asia  1952   28.801   8425333  779.445314
1  Afghanistan      Asia  1957   30.332   9240934  820.853030
2  Afghanistan      Asia  1962   31.997  10267083  853.100710
3  Afghanistan      Asia  1967   34.020  11537966  836.197138
4  Afghanistan      Asia  1972   36.088  13079460  739.981106

Now that we know how the data must be to create an animation in Python, let’s see how to create different animations in Python!

How to create animations in Python

To create animations in Python we will use the animation functions of the matplotlib module. Therefore, creating an animation is very simple and similar to creating graphics with matplotlib. We simply have to create:

  • fig : it is the object that we will use to paint our graph.
  • func : it is a function that must return the state of the animation for each frame. Basically what we have to do is create a function that returns all the graphs. Following the example of the line chart animation mentioned above, the function must return, in the first iteration a linechart with the first year, in the second interaction a linechart with the first two years, and so on for all the years.
  • interval: is the delay in milliseconds between the different frames of the animation.
  • frames: the number of images on which to base the chart. This will depend on how many “states” the animation has. If we have an animation with data in 5 different states (let’s imagine, 5 years), the number of frames will be 5, while if we have data of 100 years, the number of frames will be 100.

With these arguments, we can create all kinds of animations. Now, this can be somewhat complex (especially the update part), so I would always recommend first creating the graphic that we want and, from that, generating the animation.

In any case, we already have all the basics, so let’s see how to create animations in Python!

How to create linechart animation

As I said, the easiest way to create an animation is to first create a graphic that looks like what we want to animate. In this case it is very simple, we simply have to create a GDP per Capita linechart for the countries Spain, Italy and the United States.

Frame of linechart animation Python

Now that we have our graph, to create a line animation, we will simply have to create a function that, for each iteration, creates the line graph but for the data that we have available.

In this way, in the first iteration the line graph must create only one point for the year 1952, in the second iteration it will create the graph with the first two points (1952, 1957), and so on until completing the entire graph .

Luckily, creating this iteration having already created the graph is quite simple, since we will simply have to use the indices to define the data that the graph should take.

from matplotlib import animation

countries_plot = ['Spain', 'Italy', 'United States']
linechart_plot = gapminder.loc[gapminder['country'].isin(countries_plot), :]

# Define colors
colors = ['red', 'green', 'blue']

fig, ax = plt.subplots()

def update_linechart(i):
  for j in range(len(colors)):
    country = countries_plot[j]
    color = colors[j]

    data = linechart_plot.loc[linechart_plot['country'] == country,:]
    ax.plot(data.year[:i], data.gdpPercap[:i], color)

With this, we have already created everything we need for our animation. Now we simply have to call it using the FuncAnimation function that I have previously explained. In this tense, face

num_frames = len(linechart_plot['year'].unique())        
anim = animation.FuncAnimation(fig, update_linechart, frames = num_frames)
anim.save('linechart.gif')
Linechart animation made in Python

We already have our linechart animation created with Python! Simple, right? Now let’s see how to create barchart animations!

How to create a barchart animation in Python

A good practice to make creating our barchart (and all animations beyond linecharts) easier is to filter the data within the iteration function itself. This will make it much easier for us to create animations and it will make understanding them much easier.

Anyway, as always when we want to create an animation, we must start by graphing what we want to achieve. So, in this case I am going to create a very simple barchart in which we see how the GDP per Capita has evolved in different countries.

countries_plot = ['Spain', 'Italy', 'United States','Ireland','China']
barchart_data  = gapminder.loc[gapminder['country'].isin(countries_plot), :]

font = {
    'weight': 'normal',
    'size'  :  40,
    'color': 'lightgray'
}

colors =['#FF0000','#169b62','#008c45','#aa151b','#002868']

data_temp = barchart_data.loc[barchart_data['year'] == 2007, :]

fig, ax = plt.subplots(figsize=(10, 5))
ax.clear()
ax.barh(data_temp.country,data_temp.gdpPercap, color = colors)

ax.text(0.95, 0.2, data_temp['year'].iloc[0],
        horizontalalignment='right',
        verticalalignment='top',
        transform=ax.transAxes,
       fontdict=font)
plt.show()
Frame of barchart animation Python

Tip 1. Clean the previous graph

If we create the graph inside our ax object, the graphs will “pile up”, which can make our data not the real one. Worst of all, if you don’t use transparency in the chart, it depends on the type of chart you may not even realize.

To avoid this, in each iteration we must call ax.clear (), in such a way that it clears the result from the previous frame.

Tip 2. Filter your data in the update function

Making an animation for a single year is relatively easy. However, doing it for many years seems somewhat more complex. That is why to facilitate the creation of animations in Python I recommend:

  1. Create a list with all the possible states of the animation. In my case the states are the years, so I create a list with all the years that I can plot.
  2. Filter the complete dataset based on the state within the update function itself.

These two steps will make it much easier for you to create animations.

So, I’m going to create the update function of my barplot animation taking into account the two previous points:

countries_plot = ['Spain', 'Italy', 'United States','Ireland','China']
barchart_data  = gapminder.loc[gapminder['country'].isin(countries_plot), :]

font = {
    'weight': 'normal',
    'size'  :  40,
    'color': 'lightgray'
}

years = barchart_data['year'].unique()
colors =['#FF0000','#169b62','#008c45','#aa151b','#002868']

fig, ax = plt.subplots(figsize=(10, 5))
label = ax.text(0.95, 0.2, years[0],
            horizontalalignment='right',
            verticalalignment='top',
            transform=ax.transAxes,
            fontdict=font)

def update_barchart(i):
  year = years[i]

  data_temp = barchart_data.loc[barchart_data['year'] == year, :]
  ax.clear()
  ax.barh(data_temp.country,data_temp.gdpPercap, color = colors)
  label.set_text(year)

anim = animation.FuncAnimation(fig, update_barchart, frames = len(years))
anim.save('barchart.gif')  
Linechart animation made in Python
Linechart animation made in Python

Python barplot animation ready! As you can see, filtering the data within the update function itself makes everything much easier.

Now that we have more control over the animations, we are going to create a somewhat more complex but much more impressive animation: we are going to animate a scatter plot in Python. Let’s get to it!

How to animate Scatter Plot in Python

Once again, to create the animation of the scatter plot, first of all, create the graph for a single year. For this we are going to follow exactly the same cases as to create the barplot animation: first we filter the data and then we create the graph.

In this case, since there are many countries, I will color the countries based on the continent and, in addition, I will give them transparency:

import numpy as np
import matplotlib

fig, ax = plt.subplots(figsize=(10, 5))

scatter_data = gapminder.copy()

# Create a color depending on
conditions = [
  scatter_data.continent == 'Asia',
  scatter_data.continent == 'Europe',
  scatter_data.continent == 'Africa',
  scatter_data.continent == 'Americas',
  scatter_data.continent == 'Oceania',
]

values = list(range(5))

scatter_data['color'] = np.select(conditions, values)


font = {
    'weight': 'normal',
    'size'  :  40,
    'color': 'lightgray'
}

years = scatter_data['year'].unique()

data_temp = scatter_data.loc[scatter_data['year'] == years[-1], :]
label = ax.text(0.95, 0.25, years[-1],
            horizontalalignment='right',
            verticalalignment='top',
            transform=ax.transAxes,
            fontdict=font)

colors =[f'C{i}' for i in np.arange(1, 6)]
cmap, norm = matplotlib.colors.from_levels_and_colors(np.arange(1, 5+2), colors)

scatter = ax.scatter(data_temp.gdpPercap,
                     data_temp.lifeExp,
                     s=data_temp['pop']/500000, 
                     alpha = 0.5, 
                     c=data_temp.color, 
                     cmap=cmap,
                     norm=norm)
label.set_text(years[-1])
plt.show()
Frame of scatter plot animation Python

Now that we have our graph mounted, now we must convert it into a function to animate it. In this case, it is essential that before each frame we clean the previous content of the graphic inside the object ax, since otherwise the animation will not look good.

Beyond that, the procedure to create the scatter plot animation is the same as the one previously explained to create other types of animations in Python:


fig, ax = plt.subplots(figsize=(10, 5))

years = scatter_data['year'].unique()

colors =[f'C{i}' for i in np.arange(1, 6)]
cmap, norm = matplotlib.colors.from_levels_and_colors(np.arange(1, 5+2), colors)


label = ax.text(0.95, 0.25, years[0],
                horizontalalignment='right',
                verticalalignment='top',
                transform=ax.transAxes,
                fontdict=font)


def update_scatter(i):

    year = years[i]

    data_temp = scatter_data.loc[scatter_data['year'] == year, :]
    ax.clear()
    label = ax.text(0.95, 0.20, years[i],
                horizontalalignment='right',
                verticalalignment='top',
                transform=ax.transAxes,
                fontdict=font)
    ax.scatter(
        data_temp['gdpPercap'],
        data_temp['lifeExp'],
        s=data_temp['pop']/500000, 
        alpha = 0.5, 
        c=data_temp.color, 
        cmap=cmap,
        norm=norm
    )

    label.set_text(year)

anim = animation.FuncAnimation(fig, update_scatter, frames = len(years), interval = 30)
anim.save('scatter.gif')  
Scatter animation made in Python

We have our animated scatter plot! Now let’s go through the last of the animations that we are going to learn to create in Python: a barchart race animation.

How to create barplot race animations in Python

The barchart race animation is very similar to the barplot animation that we have done previously. The main difference lies in the fact that, in the barplot race, the data are ordered, in such a way that we can see how the top of X observations for a variable has evolved (it can be from stock market valuation to use of video games or, as in our case , the GDP per Capita of the countries).

So, to create our barplot race we will need to have, for each of the years, what is the ranking of the countries. For this, I recommend using the rank method of pandas , since we can obtain the rankings in a very simple way.

Once we have the ranking, we will simply have to filter the data to keep the number of observations that interest us, in my case 10.

Finally, once we have our data filtered, we will only have to create a horizontal bar chart where the vertical axis is the ranking. Also, to make the chart more understandable, we will change the name of the tick to the name of the country. We will do this with the tick_label parameter of the barh function.

So, let’s see what it would be like for a single case:

barchartrace_data  = gapminder.copy()
n_observations = 10

font = {
    'weight': 'normal',
    'size'  :  40,
    'color': 'lightgray'
}

data_temp = barchartrace_data.loc[barchartrace_data['year'] == 1952, :]

# Create rank and get first 10 countries
data_temp['ranking'] = data_temp.gdpPercap.rank(method='max', ascending = False).values

data_temp = data_temp.loc[data_temp['ranking'] <= n_observations]


colors = plt.cm.Dark2(range(6))

fig, ax = plt.subplots(figsize=(10, 5))
ax.barh(y = data_temp['ranking'] ,
        width = data_temp.gdpPercap, 
        tick_label=data_temp['country'],
       color=colors)
ax.text(0.95, 0.2, data_temp['year'].iloc[0],
        horizontalalignment='right',
        verticalalignment='top',
        transform=ax.transAxes,
       fontdict=font)

ax.set_ylim(ax.get_ylim()[::-1]) # Revert axis
plt.show()
Linechart animation made in Python
How to create animations in Python

Perfect! We already have our base created. Now it only remains to create our animation function. In this case, I recommend that the ranking and selection of the countries be done within the update function itself, since it greatly facilitates understanding and allows you to take advantage of the code of our base graph.

So, the update function of our barplot race is the following:

barchartrace_data  = gapminder.copy()
n_observations = 10

fig, ax = plt.subplots(figsize=(10, 5))

font = {
    'weight': 'normal',
    'size'  :  40,
    'color': 'lightgray'
}

years = barchartrace_data['year'].unique()

label = ax.text(0.95, 0.20, years[0],
                horizontalalignment='right',
                verticalalignment='top',
                transform=ax.transAxes,
                fontdict=font)


def update_barchart_race(i):

    year = years[i]

    data_temp = barchartrace_data.loc[barchartrace_data['year'] == year, :]

    # Create rank and get first 10 countries
    data_temp['prueba'] = data_temp['gdpPercap'].rank(ascending = False)
    data_temp = data_temp.loc[data_temp['prueba'] <= n_observations]

    colors = plt.cm.Dark2(range(6))

    ax.clear()
    ax.barh(y = data_temp['prueba'] ,
            width = data_temp.gdpPercap, 
            tick_label=data_temp['country'],
           color=colors)

    label = ax.text(0.95, 0.20, year,
                horizontalalignment='right',
                verticalalignment='top',
                transform=ax.transAxes,
                fontdict=font)

    ax.set_ylim(ax.get_ylim()[::-1]) # Revert axis


anim = animation.FuncAnimation(fig, update_barchart_race, frames = len(years))
anim.save('barchart_race.gif')  
How to create barchart race animations in Python

Barplot race created! We have already seen how to create different types of animations in Python. However, they don’t seem like they are entirely visual, as they simply simply put one graphic on top of the other. So, let’s see how we can improve the fluidity of our animations in Python. Let’s get to it!

How to improve the fluency of Python animations

As I explained earlier in this post, the FuncAnimation function is limited to creating the animation by putting the images generated by our update function. As our images change from year to year, our animation will make little “jumps”.

So, for our animation to be much more fluid, we will have to create more data between each of the states we have. In this way we will achieve:

  1. Have intermediate data, so the animation jump will not be so great.
  2. Have many more frames, in such a way that for the same duration of the animation, it will have more fps (frames per second) making it look much smoother.

To create this objective, we are going to do the following:

  1. Create more observations between the data we already have. These observations will be empty.
  2. Imputing data to these new empty observations by interpolation between states.

It sounds complex, but it’s easier than it sounds. Let’s see how to do it:

Create more observations between the data we already have

Creating more observations than we already have between the current states is very simple. We simply have to start with an index that goes from 0 to the number of observations we have. We can achieve this with the reset_index method.

Once our data is like this, we can simply change the current index of the data by multiplying each index by the number of frames between states that we want to create. If we want to create 10 frames, multiplying the old index by 10, the second observation (index 1) will have the index 10 and in between, many empty variables will have been created.

In any case, for the interpolation to work well, we must have the data in the proper format, which is:

  • Each row must be a state, a year in my case.
  • Each column must be the observation that we are going to graph, in my case, a country.

So, this is what we should do:

barchartrace_data  = gapminder.copy()
n_observations = 10
n_frames_between_states = 30

barchartrace_data= barchartrace_data.pivot('year', 'country', 'gdpPercap')
barchartrace_data['year'] = barchartrace_data.index

barchartrace_data.reset_index(drop = True, inplace = True)
barchartrace_data.index = barchartrace_data.index * n_frames_between_states
barchartrace_data =  barchartrace_data.reindex(range(barchartrace_data.index.max()+1))

barchartrace_data.iloc[:15,:5]
country  Afghanistan      Albania      Algeria       Angola    Argentina
0         779.445314  1601.056136  2449.008185  3520.610273  5911.315053
1                NaN          NaN          NaN          NaN          NaN
2                NaN          NaN          NaN          NaN          NaN
3                NaN          NaN          NaN          NaN          NaN
4                NaN          NaN          NaN          NaN          NaN
5                NaN          NaN          NaN          NaN          NaN
6                NaN          NaN          NaN          NaN          NaN
7                NaN          NaN          NaN          NaN          NaN
8                NaN          NaN          NaN          NaN          NaN
9                NaN          NaN          NaN          NaN          NaN
10               NaN          NaN          NaN          NaN          NaN
11               NaN          NaN          NaN          NaN          NaN
12               NaN          NaN          NaN          NaN   

As you can see, each column is a country and I have created 30 new observations among the states that I already had. Once this is done, we can see how to impute that new data through interpolation.

Imputing data to those new empty observations by interpolation between the states

To impute the empty data, we are going to use interpolation. This can be done with the interpolate method of pandas . There are different interpolation methods (you can find the method here ) and each one will give a different effect, as you can see in the following animation made by Nicholas A Rossi ( link ).

In our case we are going to make it easy, leaving the default values of the method, that is, applying a linear interpolation. Although it is the simplest, the change will be important, you just have to see the difference between using linear interpolation and not using interpolation in the animation.

So, we can interpolate our data as follows:

barchartrace_data = barchartrace_data.interpolate()
barchartrace_data.iloc[:15,:5]
country  Afghanistan      Albania      Algeria       Angola    Argentina
0         779.445314  1601.056136  2449.008185  3520.610273  5911.315053
1         780.825572  1612.430406  2467.840446  3530.854613  5942.833092
2         782.205829  1623.804677  2486.672708  3541.098952  5974.351130
3         783.586086  1635.178947  2505.504969  3551.343292  6005.869169
4         784.966343  1646.553217  2524.337230  3561.587632  6037.387208
5         786.346600  1657.927487  2543.169491  3571.831972  6068.905246
6         787.726858  1669.301758  2562.001753  3582.076311  6100.423285
7         789.107115  1680.676028  2580.834014  3592.320651  6131.941323
8         790.487372  1692.050298  2599.666275  3602.564991  6163.459362
9         791.867629  1703.424568  2618.498536  3612.809331  6194.977401
10        793.247886  1714.798839  2637.330798  3623.053670  6226.495439
11        794.628143  1726.173109  2656.163059  3633.298010  6258.013478
12        796.008401  1737.547379  2674.995320  3643.542350  6289.531517
13        797.388658  1748.921649  2693.827581  3653.786690  6321.049555
14        798.768915  1760.295920  2712.659843  3664.031029  6352.567594

Finally, now that we have our data interpolated, we are going to change the shape of our dataframe so that it continues to maintain the shape it had before, that is, that both the year, the country, and the GDP per Capita are variable. We can achieve this with the pandas melt method.

# Hacemos otro pivot para volver a los datos originales
barchartrace_data = barchartrace_data.melt(id_vars='year', var_name ='country', value_name  = 'gdpPercap')

barchartrace_data.iloc[:15,:5]
           year      country   gdpPercap
0   1952.000000  Afghanistan  779.445314
1   1952.166667  Afghanistan  780.825572
2   1952.333333  Afghanistan  782.205829
3   1952.500000  Afghanistan  783.586086
4   1952.666667  Afghanistan  784.966343
5   1952.833333  Afghanistan  786.346600
6   1953.000000  Afghanistan  787.726858
7   1953.166667  Afghanistan  789.107115
8   1953.333333  Afghanistan  790.487372
9   1953.500000  Afghanistan  791.867629
10  1953.666667  Afghanistan  793.247886
11  1953.833333  Afghanistan  794.628143
12  1954.000000  Afghanistan  796.008401
13  1954.166667  Afghanistan  797.388658
14  1954.333333  Afghanistan  798.768915

If you notice, we have a dataframe exactly the same as the one we had when we did the animation of the barchart race previously, only now we have much more intermediate data. So, we simply have to replicate the code from before to get good results:

import math

n_observations = 10

fig, ax = plt.subplots(figsize=(10, 5))

font = {
    'weight': 'normal',
    'size'  :  40,
    'color': 'lightgray'
}

years = barchartrace_data['year'].unique()

label = ax.text(0.95, 0.20, years[0],
                horizontalalignment='right',
                verticalalignment='top',
                transform=ax.transAxes,
                fontdict=font)

colors = plt.cm.Dark2(range(200))

def update_barchart_race(i):

    year = years[i]

    data_temp = barchartrace_data.loc[barchartrace_data['year'] == year, :]

    # Create rank and get first 10 countries
    data_temp['ranking'] = data_temp['gdpPercap'].rank(method = 'first',ascending = False)
    data_temp = data_temp.loc[data_temp['ranking'] <= n_observations]

    ax.clear()
    ax.barh(y = data_temp['ranking'] ,
            width = data_temp.gdpPercap, 
            tick_label=data_temp['country'],
           color=colors)

    label = ax.text(0.95, 0.20, math.floor(year),
                horizontalalignment='right',
                verticalalignment='top',
                transform=ax.transAxes,
                fontdict=font)

    ax.set_ylim(ax.get_ylim()[::-1]) # Revert axis


anim = animation.FuncAnimation(fig, update_barchart_race, frames = len(years))
anim.save('barchart_race_cool.gif', fps = 20)  
How to improve animations in Python

Improved animation! Now it looks much better, right? However, there is one thing that perhaps could be improved further: the intermediate states. And it is that, although the graphs are animated, the changes of position are still jumps. Let’s see how to create that horizontal movement of our animations in Python.

How to create horizontal movement in barchart race

The reason the positions continue to “jump” is that even though we have interpolated the data from the graph, we have not interpolated the data from the positions. So we can create an interpolation of the position data and join our previous dataset.

To do this, we will first have to keep the ranking of each country for each year. Once we have that dataframe, the process will be exactly the same as before: we pivot, interpolate and unpin the pivot with a melt.

Important: for this method to work we must use the same interpolation system that we have used previously.

ranking_data  = gapminder.copy()
n_observations = 10
n_frames_between_states = 30


#barchartrace_data['ranking'] 
ranking = ranking_data.groupby('year')['gdpPercap'].rank(method = 'first', ascending = False)
ranking = ranking.rename('ranking', axis = 1)
ranking_data = ranking_data.join(ranking)
ranking_data = ranking_data.pivot('year', 'country', 'ranking')

ranking_data['year'] =ranking_data.index
ranking_data.reset_index(drop = True, inplace = True)

ranking_data.index = ranking_data.index * n_frames_between_states
ranking_data =  ranking_data.reindex(range(ranking_data.index.max()+1))
ranking_data = ranking_data.interpolate('linear')
ranking_data = ranking_data.melt(id_vars='year', var_name ='country', value_name  = 'ranking')

ranking_data.iloc[:15,:5]
           year      country     ranking
0   1952.000000  Afghanistan  113.000000
1   1952.166667  Afghanistan  113.066667
2   1952.333333  Afghanistan  113.133333
3   1952.500000  Afghanistan  113.200000
4   1952.666667  Afghanistan  113.266667
5   1952.833333  Afghanistan  113.333333
6   1953.000000  Afghanistan  113.400000
7   1953.166667  Afghanistan  113.466667
8   1953.333333  Afghanistan  113.533333
9   1953.500000  Afghanistan  113.600000
10  1953.666667  Afghanistan  113.666667
11  1953.833333  Afghanistan  113.733333
12  1954.000000  Afghanistan  113.800000
13  1954.166667  Afghanistan  113.866667
14  1954.333333  Afghanistan  113.933333

Now that we have this information, we must join it with the information we had in the previous dataset:

barchartrace_data = ranking_data.merge(barchartrace_data, 
                                       left_on = ['country','year'], 
                                       right_on = ['country','year'])
barchartrace_data.head()
          year      country     ranking   gdpPercap
0  1952.000000  Afghanistan  113.000000  779.445314
1  1952.166667  Afghanistan  113.066667  780.825572
2  1952.333333  Afghanistan  113.133333  782.205829
3  1952.500000  Afghanistan  113.200000  783.586086
4  1952.666667  Afghanistan  113.266667  784.966343

As you can see, the rankings are not round numbers but they are slightly modified in each state. So now we can create the animation again. In this case, as we already have the calculated ranking, we will not need to recalculate it:

import math

n_observations = 10

fig, ax = plt.subplots(figsize=(10, 5))

font = {
    'weight': 'normal',
    'size'  :  40,
    'color': 'lightgray'
}

years = barchartrace_data['year'].unique()

label = ax.text(0.95, 0.20, years[0],
                horizontalalignment='right',
                verticalalignment='top',
                transform=ax.transAxes,
                fontdict=font)

# Create colors
# 1. Get continent
continent = gapminder[['country','continent']].drop_duplicates().reset_index(drop = True)

# 2. Add continent info
barchartrace_data = barchartrace_data.merge(continent,left_on = 'country', right_on = 'country')

# 3. Use continent to get color
conditions = [
  barchartrace_data['continent'] == 'Asia',
  barchartrace_data['continent'] == 'Europe',
  barchartrace_data['continent'] == 'Africa',
  barchartrace_data['continent'] == 'Americas',
  barchartrace_data['continent'] == 'Oceania',
]

values = ['#0275d8', '#5cb85c', '#5bc0de', '#f0ad4e', '#d9534f']

barchartrace_data['color'] = np.select(conditions, values)


def update_barchart_race(i):

    year = years[i]

    data_temp = barchartrace_data.loc[barchartrace_data['year'] == year, :]

    # Create rank and get first 10 countries
    data_temp = data_temp.loc[data_temp['ranking'] <= n_observations]

    ax.clear()
    ax.barh(y = data_temp['ranking'] ,
            width = data_temp.gdpPercap, 
            tick_label=data_temp['country'],
           color=data_temp['color'])

    label = ax.text(0.95, 0.20, math.floor(year),
                horizontalalignment='right',
                verticalalignment='top',
                transform=ax.transAxes,
                fontdict=font)

    ax.set_ylim(ax.get_ylim()[::-1]) # Revert axis


anim = animation.FuncAnimation(fig, update_barchart_race, frames = len(years), )
anim.save('barchart_race_cool2.gif', fps=30)  
How to improve animations in Python

We already have our animation created in Python! As you can see, the interpolation has made our animation much more attractive and seem much more fluid than it was at the beginning. And all, with just a few lines of code! Isn’t it fantastic? But that’s not all, let’s see another little trick that will allow us to improve our animations in Python a lot.

Avoid jumps in animations

A typical problem in animations, as has happened in the animation of the scatter plot, is that there are jumps between frames. This is because the axes of the graph change, making the content of the graph appear different when, in fact, it is not.

Fixing this is pretty easy. Simply, for each frame, the maximum value of the X and Y axes must be set. This value will be the maximum that the graph will reach in the entire series. In this way, we will be able to avoid those jerks.

This is especially important to apply when animating graphics like the scatter plot. However, when animating the linechart, it is not recommended to apply it as it greatly reduces the visual impact of the animation.

So, we are going to redo the animation of the scatter plot, but this time applying greater fluidity through interpolation and avoiding jumps by setting the scales.

In this case, we will have to apply the interpolation to the three variables that are used in the animation. So, to facilitate the process, we will create a function that does the interpolation for us.

scatter_data = gapminder.copy()
n_frames_between_states = 30
continent = gapminder[['country','continent']].drop_duplicates().reset_index(drop = True)


def interpolate_data(data,frame,obs,variable, n_new_frames, interpolation = 'linear'):
    data= data.pivot(frame, obs, variable)
    data[frame] = data.index
    data.reset_index(drop = True, inplace = True)
    data.index = data.index * n_new_frames
    data =  data.reindex(range(data.index.max()+1))
    data = data.interpolate(interpolation)
    data = data.melt(id_vars= frame, var_name = obs, value_name  = variable)
    return data

# Interpolate data
scatter_data_pop = interpolate_data(scatter_data, 'year', 'country','pop',30)
scatter_data_gdpPerCap = interpolate_data(scatter_data, 'year', 'country','gdpPercap',30)
scatter_data_lifeExp = interpolate_data(scatter_data, 'year', 'country','lifeExp',30)

# Merge the datasets
scatter_data = scatter_data_gdpPerCap.merge(scatter_data_pop,
                   left_on = ['country','year'], 
                   right_on = ['country','year'])

scatter_data = scatter_data.merge(scatter_data_lifeExp,
                   left_on = ['country','year'], 
                   right_on = ['country','year']).merge(continent)

scatter_data.head()
	year	        country	        gdpPercap	pop	        lifeExp	       continent
0	1952.000000	Afghanistan	779.445314	8425333.0	28.801000	Asia
1	1952.166667	Afghanistan	780.825572	8452519.7	28.852033	Asia
2	1952.333333	Afghanistan	782.205829	8479706.4	28.903067	Asia
3	1952.500000	Afghanistan	783.586086	8506893.1	28.954100	Asia
4	1952.666667	Afghanistan	784.966343	8534079.8	29.005133	Asia

Now that we have the dataset, we can create the animation. To set the limits of the animation we simply have to use the set_xlim method.

fig, ax = plt.subplots(figsize=(10, 5))

years = scatter_data['year'].unique()

conditions = [
  scatter_data.continent == "Asia",
  scatter_data.continent == "Europe",
  scatter_data.continent == "Africa",
  scatter_data.continent == "Americas",
  scatter_data.continent == "Oceania",
]

values = list(range(5))

scatter_data['color'] = np.select(conditions, values)
colors =[f"C{i}" for i in np.arange(1, 6)]
cmap, norm = matplotlib.colors.from_levels_and_colors(np.arange(1, 5+2), colors)

# Get maximum values
x_max = scatter_data['gdpPercap'].max()
y_max = scatter_data['lifeExp'].max()


  
def update_scatter(i):
    
    year = years[i]
    
    data_temp = scatter_data.loc[scatter_data['year'] == year, :]
    ax.clear()
    
    
    label = ax.text(0.95, 0.25, years[0],
                    horizontalalignment='right',
                    verticalalignment='top',
                    transform=ax.transAxes,
                    fontdict=font)
    
    # Set limits
    ax.set_xlim((0,x_max))
    ax.set_ylim((0,y_max))
    
    ax.scatter(
        data_temp['gdpPercap'],
        data_temp['lifeExp'],
        s=data_temp['pop']/500000, 
        alpha = 0.5, 
        c=data_temp.color, 
        cmap=cmap,
        norm=norm
    )
    
    label.set_text(math.floor(year))
    
anim = animation.FuncAnimation(fig, update_scatter, frames = len(years))
anim.save('scatter2.gif', fps = 20)
Scatter animation made in Python

Conclusion

Without a doubt creating animations in Python is something that will allow you to create highly visual graphics that generate much more impact. This is something basic that will allow you from generating much more powerful to be able to explain processes in a simpler way, as I did with the k-Mean algorithm in this post .

Also, if you are used to working with pandas and matplotlib and you understand the workings behind the animation functions, it is something very simple to do.

I hope you liked this post. If so, I encourage you to subscribe to keep up to date with all the posts that I am uploading. In any case, see you next time!