Build Animation#
In this tutorial, we will create a bar chart race using the Altair plotting library and the Player
widget.
Dependencies
altair panel
Code
import altair as alt
import pandas as pd
import panel as pn
pn.extension("vega") # load the vega/ Altair JavaScript dependencies
BY = "t_manu" # t_state, t_county, or t_manu
VALUE = "p_cap"
## Extract the data
@pn.cache() # use caching to only download data once
def get_data():
return pd.read_csv("https://assets.holoviz.org/panel/tutorials/turbines.csv.gz")
## Transform the data
data = get_data()
min_year = int(data.p_year.min())
max_year = int(data.p_year.max())
max_capacity_by_manufacturer = data.groupby(BY)[VALUE].sum().max()
def get_group_sum(year):
return (
data[data.p_year <= year][[BY, VALUE]]
.groupby(BY)
.sum()
.sort_values(by=VALUE, ascending=False)
.reset_index()
.head(10)
)
# Plot the data
def get_plot(year):
data = get_group_sum(year)
base = (
alt.Chart(data)
.encode(
x=alt.X(VALUE, scale=alt.Scale(domain=[0, max_capacity_by_manufacturer]), title="Capacity"),
y=alt.Y(f"{BY}:O", sort=[BY], title="Manufacturer"),
text=alt.Text(VALUE, format=",.0f"),
)
.properties(title=str(year), height=500, width="container")
)
return base.mark_bar() + base.mark_text(align="left", dx=2)
# Add widgets
year = pn.widgets.Player(
value=max_year,
start=min_year,
end=max_year,
name="Year",
loop_policy="loop",
interval=300,
align="center",
)
def pause_player_at_max_year(value):
if year.value==max_year:
year.pause()
pn.bind(pause_player_at_max_year, year, watch=True) # Stops the player when max_year is reached.
# Bind the plot to the Player widget
plot = pn.pane.Vega(pn.bind(get_plot, year))
# Layout the components
pn.Column("# Wind Turbine Capacity 1982-2022", plot, year, sizing_mode="stretch_width").servable()
Install the dependencies#
Please ensure that Altair and Panel are installed.
pip install altair panel
conda install -y -c conda-forge altair panel
Explanation#
๐ Welcome to the Altair & Panel Bar Chart Race Tutorial!
Letโs embark on an adventure to visualize the dynamic evolution of wind turbine capacities over the years. ๐ฌ๏ธ๐จ
import altair as alt
import pandas as pd
import panel as pn
pn.extension("vega")
First things first, we need to import our trusty companions: Altair for stunning visualizations, Pandas for data manipulation, and Panel for creating interactive web apps.
๐ Step 1: Data Exploration and Extraction
BY = "t_manu"
VALUE = "p_cap"
@pn.cache()
def get_data():
return pd.read_csv("https://datasets.holoviz.org/windturbines/v1/windturbines.csv.gz")
data = get_data()
min_year = int(data.p_year.min())
max_year = int(data.p_year.max())
max_capacity_by_manufacturer = data.groupby(BY)[VALUE].sum().max()
We begin by defining our data source and understanding its bounds. We extract turbine data and identify the minimum and maximum years present in the dataset, along with the maximum capacity value aggregated by manufacturer.
๐ Step 2: Data Transformation and Grouping
def get_group_sum(year):
return (
data[data.p_year <= year][[BY, VALUE]]
.groupby(BY)
.sum()
.sort_values(by=VALUE, ascending=False)
.reset_index()
.head(10)
)
We create a function to aggregate the turbine data up to a specified year, grouping by manufacturer and summing the capacity values. We then select the top 10 manufacturers based on capacity.
๐ Step 3: Plotting Function
def get_plot(year):
data = get_group_sum(year)
base = (
alt.Chart(data)
.encode(
x=alt.X(VALUE, scale=alt.Scale(domain=[0, max_capacity_by_manufacturer]), title="Capacity"),
y=alt.Y(f"{BY}:O", sort=[BY], title="Manufacturer"),
text=alt.Text(VALUE, format=",.0f"),
)
.properties(title=str(year), height=500, width="container")
)
return base.mark_bar() + base.mark_text(align="left", dx=2)
Time to bring our visualization to life! This function generates an Altair chart based on the grouped data for a given year, encoding capacity on the x-axis, manufacturer on the y-axis, and displaying capacity as text within the bars.
โฉ Step 4: Adding Interactive Controls
year = pn.widgets.Player(
value=max_year,
start=min_year,
end=max_year,
name="Year",
loop_policy="loop",
interval=300,
align="center",
)
def pause_player_at_max_year(value):
if year.value==max_year:
year.pause()
pn.bind(pause_player_at_max_year, year, watch=True); # semi-colon is only needed when line is run at end of cell
Letโs make it interactive! We introduce a Player
widget to loop continuously through the years.
๐จ Step 5: Binding Plot to Widget
plot = pn.pane.Vega(pn.bind(get_plot, year))
Now, letโs bind our plot function to the selected year. Whenever the user changes the year, the plot dynamically updates to reflect the selected timeframe.
๐ผ๏ธ Step 6: Layout and Presentation
pn.Column("# Wind Turbine Capacity 1982-2022", plot, year, sizing_mode="stretch_width").servable()
Lastly, we organize our components into a neat layout. A title sets the stage, followed by our interactive plot and the year selection widget. The layout adjusts to fill the available width.
Voilร ! ๐ Youโre now ready to run the code and witness the mesmerizing bar chart race showcasing the evolution of wind turbine capacities over the years. Happy exploring! ๐
Hint
You can create many types of animations using the object types that Panel can display. Not just bar chart races using Altair.
Serve the App#
Now serve the app with:
panel serve app.py --dev
panel serve app.ipynb --dev
Open http://localhost:5006/app
Press the play button.
It should resemble
Recap#
In this tutorial, we built a bar chart race using the Altair plotting library and the Player
widget.