Hello, I am happy to share tutorial for animated graphs in Plotly, where I used a full potential of this amazing library.
Creation of such graphs was an amazing coding experience. Please let me know, if you like them
2 Likes
@TomDuricek
What an amazing article. Thank you so much for writing it and sharing with us.
The code below (taken from Tomβs repo) will generate two charts. This is one of them.
Hereβs the full .py code in case people are interested:
import imageio
import numpy as np
import pandas as pd
from io import BytesIO
import plotly.express as px
import plotly.graph_objects as go
df = pd.read_csv('https://raw.githubusercontent.com/tomasduricek/animated-graphs/main/retail_data.csv')
df['date'] = pd.to_datetime(df['date']
.astype(str)
.str[:7],
format="%Y-%m") # Control ChatGPT date format output
df = df.sort_values(['date', 'retail_group'], ignore_index=True)
RETAIL_GROUP_COLORS = ['#1F4068', '#527A82', '#DE8918', '#BF3200']
FIRST_DAY_OF_ALL_YEARS = df[df['date'].dt.month == 1]['date'].unique()
N_UNIQUE_RETAIL_GROUPS = df['retail_group'].nunique()
df_indexed = pd.DataFrame()
for index in np.arange(start=0,
stop=len(df)+1,
step=N_UNIQUE_RETAIL_GROUPS):
df_slicing = df.iloc[:index].copy()
df_slicing['frame'] = (index//N_UNIQUE_RETAIL_GROUPS)
df_indexed = pd.concat([df_indexed, df_slicing])
# Scatter Plot
scatter_plot = px.scatter(
df_indexed,
x='date',
y='average_monthly_income',
color='retail_group',
animation_frame='frame',
color_discrete_sequence=RETAIL_GROUP_COLORS
)
for frame in scatter_plot.frames:
for data in frame.data:
data.update(mode='markers',
showlegend=True,
opacity=1)
data['x'] = np.take(data['x'], [-1])
data['y'] = np.take(data['y'], [-1])
# Line Plot
line_plot = px.line(
df_indexed,
x='date',
y='average_monthly_income',
color='retail_group',
animation_frame='frame',
color_discrete_sequence=RETAIL_GROUP_COLORS,
width=1000,
height=500,
line_shape='spline' # make a line graph curvy
)
line_plot.update_traces(showlegend=False) # legend will be from line graph
for frame in line_plot.frames:
for data in frame.data:
data.update(mode='lines', opacity=0.8, showlegend=False)
# Stationary combined plot
combined_plot = go.Figure(
data=line_plot.data + scatter_plot.data,
frames=[
go.Frame(data=line_plot.data + scatter_plot.data, name=scatter_plot.name)
for line_plot, scatter_plot in zip(line_plot.frames, scatter_plot.frames)
],
layout=line_plot.layout
)
combined_plot.update_yaxes(
gridcolor='#7a98cf',
griddash='dot',
gridwidth=.5,
linewidth=2,
tickwidth=2
)
combined_plot.update_xaxes(
title_font=dict(size=16),
linewidth=2,
tickwidth=2
)
combined_plot.update_traces(
line=dict(width=5),
marker=dict(size=25))
combined_plot.update_layout(
font=dict(size=18),
yaxis=dict(tickfont=dict(size=16)),
xaxis=dict(tickfont=dict(size=16)),
showlegend=True,
legend=dict(
title='Retail group'),
template='simple_white',
title="<b>Monthly Retail Prices During Covid-19</b>",
yaxis_title="<b>Average price (k)</b>",
xaxis_title="<b>Date</b>",
yaxis_showgrid=True,
xaxis_range=[df_indexed['date'].min() + pd.DateOffset(months=3),
df_indexed['date'].max() + pd.DateOffset(months=3)],
yaxis_range=[df_indexed['average_monthly_income'].min() * 0.75,
df_indexed['average_monthly_income'].max() * 1.25],
plot_bgcolor='#fffcf7',
paper_bgcolor='#fffcf7',
title_x=0.5
)
# adjust speed of animation
combined_plot['layout'].pop("sliders")
combined_plot.layout.updatemenus[0].buttons[0]['args'][1]['frame']['duration'] = 120
combined_plot.layout.updatemenus[0].buttons[0]['args'][1]['transition']['duration'] = 50
combined_plot.layout.updatemenus[0].buttons[0]['args'][1]['transition']['redraw'] = False
combined_plot.show()
# Autoscaled Combined Plot
combined_plot = go.Figure(
data=line_plot.data + scatter_plot.data,
frames=[
frame.update(
layout={
'xaxis': {'range': [min(frame.data[0].x),
max(frame.data[0].x + pd.DateOffset(months=2))],
'tickformat': '%Y',
'tickangle': 0,
'tickvals': FIRST_DAY_OF_ALL_YEARS},
'yaxis': {'range': [min(frame.data[0].y) * 0.3,
max(frame.data[0].y) * 1.5],
'nticks': 6},
}
)
for frame in combined_plot.frames
],
layout=line_plot.layout
)
combined_plot.update_yaxes(
gridcolor='#7a98cf',
griddash='dot',
gridwidth=.5,
linewidth=2,
tickwidth=2
)
combined_plot.update_xaxes(
title_font=dict(size=16),
linewidth=2,
tickwidth=2
)
combined_plot.update_traces(
line=dict(width=5),
marker=dict(size=25))
combined_plot.update_layout(
font=dict(size=18),
yaxis=dict(tickfont=dict(size=16)),
xaxis=dict(tickfont=dict(size=16)),
showlegend=True,
legend=dict(
title='Retail group'),
template='simple_white',
title="<b>Monthly Retail Prices During Covid-19</b>",
yaxis_title="<b>Average price (k)</b>",
xaxis_title="<b>Date",
yaxis_showgrid=True,
xaxis_range=[df_indexed['date'].min() + pd.DateOffset(months=3),
df_indexed['date'].max() + pd.DateOffset(months=3)],
yaxis_range=[df_indexed['average_monthly_income'].min() * 0.75,
df_indexed['average_monthly_income'].max() * 1.25],
plot_bgcolor='#fffcf7',
paper_bgcolor='#fffcf7',
title_x=0.5
)
# adjust speed of animation
combined_plot['layout'].pop("sliders")
combined_plot.layout.updatemenus[0].buttons[0]['args'][1]['frame']['duration'] = 150
combined_plot.layout.updatemenus[0].buttons[0]['args'][1]['transition']['duration'] = 120
combined_plot.layout.updatemenus[0].buttons[0]['args'][1]['transition']['redraw'] = False
combined_plot.show()
1 Like