Show Sidebar Hide Sidebar

Adding Sliders to Animations in Python

How to make the classic Gapminder Animation using sliders and buttons in Python.

New to Plotly?¶

Plotly's Python library is free and open source! Get started by downloading the client and reading the primer.
You can set up Plotly to work in online or offline mode, or in jupyter notebooks.
We also have a quick-reference cheatsheet (new!) to help you get started!

Version Check¶

Note: Animations are available in version 1.12.10+ Run pip install plotly --upgrade to update your Plotly version.

In [1]:
import plotly
plotly.__version__
Out[1]:
'2.0.15'

Import Data¶

This tutorial walks you through how to make an example using the Gapminder dataset to present the GDP per Capita vs Life Expectancy across the years 1952 to 2007 in an animated Bubble Chart, in which the bubbles represent countries and their sizes represent the population.

First import the Gapminder data that we will be using for the example and store in a dataframe:

In [5]:
import plotly.plotly as py
from plotly.grid_objs import Grid, Column
from plotly.tools import FigureFactory as FF 

import pandas as pd
import time

url = 'https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv'
dataset = pd.read_csv(url)

table = FF.create_table(dataset.head(10))
py.iplot(table, filename='animations-gapminder-data-preview')
Out[5]:

Make the Grid¶

Since we are using the v2 api for animations in Plotly, we need to first make a grid. You can learn more in the introduction to animation doc.

We will first define a list of string years which will represent the values that our slider will take on. Going through the dataset, we will take out all the unique continents from the column continent and store them as well. Finally, we make a grid with each column representing a slice of the dataframe by year, continent and column name, making sure to name each column uniquly by these variables:

In [6]:
years_from_col = set(dataset['year'])
years_ints = sorted(list(years_from_col))
years = [str(year) for year in years_ints]
years.remove('1957')

# make list of continents
continents = []
for continent in dataset['continent']:
    if continent not in continents: 
        continents.append(continent)

columns = []
# make grid
for year in years:
    for continent in continents:
        dataset_by_year = dataset[dataset['year'] == int(year)]
        dataset_by_year_and_cont = dataset_by_year[dataset_by_year['continent'] == continent]
        for col_name in dataset_by_year_and_cont:
            # each column name is unique
            column_name = '{year}_{continent}_{header}_gapminder_grid'.format(
                year=year, continent=continent, header=col_name
            )
            a_column = Column(list(dataset_by_year_and_cont[col_name]), column_name)
            columns.append(a_column)

# upload grid
grid = Grid(columns)
url = py.grid_ops.upload(grid, 'gapminder_grid'+str(time.time()), auto_open=False)
url
Out[6]:
u'https://plot.ly/~PythonPlotBot/2476/'

Make the Figure¶

In [7]:
figure = {
    'data': [],
    'layout': {},
    'frames': [],
    'config': {'scrollzoom': True}
}

# fill in most of layout
figure['layout']['xaxis'] = {'range': [30, 85], 'title': 'Life Expectancy', 'gridcolor': '#FFFFFF'}
figure['layout']['yaxis'] = {'title': 'GDP per Capita', 'type': 'log', 'gridcolor': '#FFFFFF'}
figure['layout']['hovermode'] = 'closest'
figure['layout']['plot_bgcolor'] = 'rgb(223, 232, 243)'

Add Slider¶

For the slider to appear, we need to adda sliders dictionary to layout. The sliders dictionary is set in the following way:

figure['layout']['sliders] = {
    'active': 0,
    'yanchor': 'top',
    'xanchor': 'left',
    'currentvalue': {
        'font': {'size': 20},
        'prefix': 'text-before-value-on-display',
        'visible': True,
        'xanchor': 'right'
    },
    'transition': {'duration': 300, 'easing': 'cubic-in-out'},
    'pad': {'b': 10, 't': 50},
    'len': 0.9,
    'x': 0.1,
    'y': 0,
    'steps': [...]
}
  • yanchor determines whether the slider is on the top or bottom of the chart page
  • xanchor is similar, only with left and right as possible values
  • currentvalue sets the display of the current value that the slider is hovering on. It contains args such as prefix, which sets the text that appears before the value.
  • steps is a list of dictionaries each of which corresponds to a frame in the figure. They should be ordered in the sequence in which the frames occur in the animation.

Each dictionary in steps has the following form:

{
    'method': 'animate',
    'label': 'label-for-frame',
    'value': 'value-for-frame(defaults to label)',
    'args': [{'frame': {'duration': 300, 'redraw': False},
         'mode': 'immediate'}
    ],
}
  • the first item in the list args is a list containing the slider-value of that frame
  • label is the text that appears next to the prefix arg mentioned in the slider section (eg. Year: 1952)

For more information, check out the documentation.

In [8]:
sliders_dict = {
    'active': 0,
    'yanchor': 'top',
    'xanchor': 'left',
    'currentvalue': {
        'font': {'size': 20},
        'prefix': 'Year:',
        'visible': True,
        'xanchor': 'right'
    },
    'transition': {'duration': 300, 'easing': 'cubic-in-out'},
    'pad': {'b': 10, 't': 50},
    'len': 0.9,
    'x': 0.1,
    'y': 0,
    'steps': []
}

Add Play and Pause Buttons¶

In [9]:
figure['layout']['updatemenus'] = [
    {
        'buttons': [
            {
                'args': [None, {'frame': {'duration': 500, 'redraw': False},
                         'fromcurrent': True, 'transition': {'duration': 300, 'easing': 'quadratic-in-out'}}],
                'label': 'Play',
                'method': 'animate'
            },
            {
                'args': [[None], {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate',
                'transition': {'duration': 0}}],
                'label': 'Pause',
                'method': 'animate'
            }
        ],
        'direction': 'left',
        'pad': {'r': 10, 't': 87},
        'showactive': False,
        'type': 'buttons',
        'x': 0.1,
        'xanchor': 'right',
        'y': 0,
        'yanchor': 'top'
    }
]

custom_colors = {
    'Asia': 'rgb(171, 99, 250)',
    'Europe': 'rgb(230, 99, 250)',
    'Africa': 'rgb(99, 110, 250)',
    'Americas': 'rgb(25, 211, 243)',
    'Oceania': 'rgb(50, 170, 255)'
}

Fill in Figure with Data and Frames¶

Now we can put the data from our grid into the figure. Since we are using referenced data from the grid, we use the .get_column_reference() method on the grid and supply the name of the column we want via a looping through all the years and continents. First we

Note: If you are using referenced data for a particular parameter, you MUST change the parameter name from name to namesrc to indicate that you are using referenced data from a grid. For instance, x becomes xsrc, text becomes textsrc, etc.

In [10]:
col_name_template = '{year}_{continent}_{header}_gapminder_grid'
year = 1952
for continent in continents:
    data_dict = {
        'xsrc': grid.get_column_reference(col_name_template.format(
            year=year, continent=continent, header='lifeExp'
        )),
        'ysrc': grid.get_column_reference(col_name_template.format(
            year=year, continent=continent, header='gdpPercap'
        )),
        'mode': 'markers',
        'textsrc': grid.get_column_reference(col_name_template.format(
            year=year, continent=continent, header='country'
        )),
        'marker': {
            'sizemode': 'area',
            'sizeref': 200000,
            'sizesrc': grid.get_column_reference(col_name_template.format(
                 year=year, continent=continent, header='pop'
            )),
            'color': custom_colors[continent]
        },
        'name': continent
    }
    figure['data'].append(data_dict)

Create Frames¶

Finally we make our frames. Here we are running again through the years and continents, but for each combination we instantiate a frame dictionary of the form:

frame = {'data': [], 'name': value-name}

We add a dictionary of data to this list and at the end of each loop, we ensure to add the steps dictionary to the steps list. At the end, we attatch the sliders dictionary to the figure via:

figure['layout']['sliders'] = [sliders_dict]

and then we plot! Enjoy the Gapminder example!

In [11]:
for year in years:
    frame = {'data': [], 'name': str(year)}
    for continent in continents:
        data_dict = {
            'xsrc': grid.get_column_reference(col_name_template.format(
                year=year, continent=continent, header='lifeExp'
            )),
            'ysrc': grid.get_column_reference(col_name_template.format(
                year=year, continent=continent, header='gdpPercap'
            )),
            'mode': 'markers',
            'textsrc': grid.get_column_reference(col_name_template.format(
                year=year, continent=continent, header='country'
                )),
            'marker': {
                'sizemode': 'area',
                'sizeref': 200000,
                'sizesrc': grid.get_column_reference(col_name_template.format(
                    year=year, continent=continent, header='pop'
                )),
                'color': custom_colors[continent]
            },
            'name': continent
        }
        frame['data'].append(data_dict)

    figure['frames'].append(frame)
    slider_step = {'args': [
        [year],
        {'frame': {'duration': 300, 'redraw': False},
         'mode': 'immediate',
       'transition': {'duration': 300}}
     ],
     'label': year,
     'method': 'animate'}
    sliders_dict['steps'].append(slider_step)

figure['layout']['sliders'] = [sliders_dict]

Plot Animation¶

In [12]:
py.icreate_animations(figure, 'gapminder_example'+str(time.time()))
Out[12]:

Reference¶

For additional information and attributes for creating bubble charts in Plotly see: https://plot.ly/python/bubble-charts/. For more documentation on creating animations with Plotly, see https://plot.ly/python/#animations.

Still need help?
Contact Us

For guaranteed 24 hour response turnarounds, upgrade to a Developer Support Plan.