Line Chart Animation with Plotly on Jupyter | by Shinichi Okada | Towards Data Science

Table of ContentsIntroduction1. Data preparation2. All countries line chart3. Adding LOG and LINEAR buttons4. Changing hovermode5. Line chart animation6. Scatter and bar chart animationsConclusion
In this article, I will try to reproduce one of Our World in Data charts using Plotly animation on Jupyter.
ourworldindata.org is an excellent website and I really like their visualizations.
The above chart can animate the graph, highlight lines, select different cases on the right side, select LINEAR and LOG buttons, and select countries you want to display.
The project is an open-source project and you can explore their GitHub repo.
It is a JavaScript-based app, using MySQL as a database and React, Mobx, TypeScript, Node, Express, D3, and other JavaScripts.
Plotly supports creating animations, buttons, and selectable lines.
However, current Plotly animations have some limitations, and smooth inter-frame transitions are only possible for a scatter and bar chart. It does not support a checkbox, even though PlotlyDash which is a Python framework for building web applications supports Checklist.
This article assumes you are already familiar with the basic operation in JupyterLab/Jupyter Notebook.
Plotly installation
plotly.py may be installed using pip.
$ pip install plotly==4.8.1
or conda.
$ conda install -c plotly plotly=4.8.1
Using pip:
$ pip install jupyterlab "ipywidgets>=7.5"
or Conda:
$ conda install jupyterlab "ipywidgets=7.5"
Then run the following (you need node installed):
# JupyterLab renderer support $ jupyter labextension install jupyterlab-plotly@4.8.1  # OPTIONAL: Jupyter widgets extension $ jupyter labextension install @jupyter-widgets/jupyterlab-manager plotlywidget@4.8.1
towardsdatascience.com
towardsdatascience.com
towardsdatascience.com
You can download the dataset from Our World in Data or this repo.
import pandas as pdimport plotly.express as pxdf = pd.read_csv('https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv')display(df.head())display(df.shape)
import pandas as pdimport plotly.express as pxdf = pd.read_csv('https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv') excludes = ["World", "Africa", "North America", "South America", "Asia", "Europe", "European Union", "High income", "Low income", "Lower middle income", "Oceania", "Upper middle income", "World excl. China", "World excl. China and South Korea", "International",            "World excl. China, South Korea, Japan and Singapore","Asia excl. China"] df=df[~df['Entity'].isin(excludes)]df.columns = ['Country','Code','Date','Confirmed','Days since confirmed']df['Date']= pd.to_datetime(df['Date']).dt.strftime('%Y-%m-%d')df_all=df[(df['Date']>'2020-03-01') & (df['Date'] <'2020-06-14')]
Import necessary libraries, Pandas, and Plotly.express. Read a comma-separated values(csv) file into DataFrame.
excludes is a list of names in the Entity column that we want to exclude.
isin method finds excludes values in Entity column and we use ~ to exclude these rows.
We rename the column names to Country, Code, Date, Confirmed, Days since confirmed.
We change the data type of Date column from object to datetime.We also format it to Year-Month-Day.
We select rows from 2020-03-01 to 2020-06-14 in theDate column.
display(df.head())display(df.shape)
Let’s create a line chart using df_all. We set Date for the x-axis, Confirmed for the y-axis and use Country for line colors.
fig = px.line(df_all, x="Date", y="Confirmed", color="Country")fig.show()
The chart has the ability to reveal more information about a data point by moving a mouse cursor over the point. We can hide or show lines by clicking country names on the right side.
We are going to add two buttons that change the y-axis scale.
fig = px.line(df_all, x="Date", y="Confirmed", color="Country") fig.update_layout(    updatemenus=[        dict(            type = "buttons",            direction = "left",            buttons=list([                dict(                    args=[{"yaxis.type": "linear"}],                    label="LINEAR",                    method="relayout"                ),                dict(                    args=[{"yaxis.type": "log"}],                    label="LOG",                    method="relayout"                )            ]),        ),    ])fig.show()
Using theupdate_layout method to update the properties of the figure’s layout. Adding two buttons, LINEAR and LOG and changing the yaxis.type to linear and log accordingly.
Plotly has three different hovermode. The default setting is hovermode='closest', as we can see in the previous image, a single hover label appears for the point directly underneath the cursor.
For this chart, we are going to use a unified hovermode='x unified'. It shows a single hover label with all the data information. For clarity, we select four countries.
import numpy as npimport pandas as pdimport plotly.graph_objects as go  #plotly 4.0.0rc1 df = pd.read_csv('https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv')df.columns = ['Country','Code','Date','Confirmed','Days since confirmed']df['Date']= pd.to_datetime(df['Date']).dt.strftime('%Y-%m-%d')df=df[(df['Date']>'2020-03-15') & (df['Date'] <'2020-06-14')] includes=['United States','Russia', 'India','Brazil']df_selected=df[df['Country'].isin(includes)] fig = px.line(df_selected, x="Date", y="Confirmed", color="Country") fig.update_layout(    hovermode='x unified',    updatemenus=[        dict(            type = "buttons",            direction = "left",            buttons=list([                dict(                    args=[{"yaxis.type": "linear"}],                    label="LINEAR",                    method="relayout"                ),                dict(                    args=[{"yaxis.type": "log"}],                    label="LOG",                    method="relayout"                )            ]),        ),    ])fig.show()
Plotly animations have some limitations and the line chart is not supported.
Line chart animation can be done but it is not as neat as other animations such as those for bar and scatter charts.
import numpy as npimport pandas as pdimport plotly.graph_objects as godf = pd.read_csv('https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv')df.columns = ['Country','Code','Date','Confirmed','Days since confirmed']df['Date']= pd.to_datetime(df['Date']).dt.strftime('%Y-%m-%d')df=df[(df['Date']>'2020-03-15') & (df['Date'] <'2020-06-14')]usa=df[df['Country'].isin(['United States'])]brazil=df[df['Country'].isin(['Brazil'])]india=df[df['Country'].isin(['India'])]russia=df[df['Country'].isin(['Russia'])]trace1 = go.Scatter(x=usa['Date'][:2],                    y=usa['Confirmed'][:2],                    mode='lines',                    line=dict(width=1.5))trace2 = go.Scatter(x = brazil['Date'][:2],                    y = brazil['Confirmed'][:2],                    mode='lines',                    line=dict(width=1.5))trace3 = go.Scatter(x = india['Date'][:2],                    y = india['Confirmed'][:2],                    mode='lines',                    line=dict(width=1.5))trace4 = go.Scatter(x = russia['Date'][:2],                    y = russia['Confirmed'][:2],                    mode='lines',                    line=dict(width=1.5))frames = [dict(data= [dict(type='scatter',                           x=usa['Date'][:k+1],                           y=usa['Confirmed'][:k+1]),                      dict(type='scatter',                           x=brazil['Date'][:k+1],                           y=brazil['Confirmed'][:k+1]),                      dict(type='scatter',                           x=india['Date'][:k+1],                           y=india['Confirmed'][:k+1]),                      dict(type='scatter',                           x=russia['Date'][:k+1],                           y=russia['Confirmed'][:k+1]),                     ],               traces= [0, 1, 2, 3],                )for k  in  range(1, len(usa)-1)]layout = go.Layout(width=700,                   height=600,                   showlegend=False,                   hovermode='x unified',                   updatemenus=[                        dict(                            type='buttons', showactive=False,                            y=1.05,                            x=1.15,                            xanchor='right',                            yanchor='top',                            pad=dict(t=0, r=10),                            buttons=[dict(label='Play',                            method='animate',                            args=[None,                                   dict(frame=dict(duration=3,                                                   redraw=False),                                                  transition=dict(duration=0),                                                  fromcurrent=True,                                                  mode='immediate')]                            )]                        ),                        dict(                            type = "buttons",                            direction = "left",                            buttons=list([                                dict(                                    args=[{"yaxis.type": "linear"}],                                    label="LINEAR",                                    method="relayout"                                ),                                dict(                                    args=[{"yaxis.type": "log"}],                                    label="LOG",                                    method="relayout"                                )                            ]),                        ),                    ]                                )layout.update(xaxis =dict(range=['2020-03-16', '2020-06-13'], autorange=False),              yaxis =dict(range=[0, 35000], autorange=False));fig = go.Figure(data=[trace1, trace2, trace3, trace4], frames=frames, layout=layout)fig.show()
We can add more countries if we want.
We create four separate DataFrame. You can increase or decrease the number of countries.
We create four traces with own x, y, mode, and line values. If you change the number of countries, you need to adjust here as well.
We create frames with data list values. traces=[0,1,2,3] is used to iterate frame[k]['data'][0], frame[k]['data'][1], etc. If you change the number of countries, again, you need to adjust the code here. e.g. traces=[0,1,2,3,4,5].
We set width, height, hovermode, and updatemenus. We set two kinds of buttons, one for play and others for LINEAR and LOG.
update updates the properties of the figure with a dict with keyword arguments.
We create a new Figure instance and add all the traces we created in line 15–33. If you change the number of countries, you need to update this line.
Plotly supports the scatter and bar chart animations.
You can find the code here.
You can drop the animation buttons by adding fig["layout"].pop("updatemenus"), and use it as a slider.
Code
In a bar chart animation, we can hover to see more detailed information and we can hide/show items by clicking on the right menu.
Code
It was not exactly the same as Our World in Data Website, but we learned how to create line charts, line chart animations, add unified hover mode, and add buttons.
Currently, Plotly requires more code to make a line chart animation. Also, it would be great if the scatter chart could draw trace lines and a speed control parameter for the animation.
Get full access to every story on Medium by becoming a member.