Build a Dashboard#
Welcome to our tutorial on building an interactive dashboard showcasing essential metrics from wind turbine manufacturers.
We’ll demonstrate how to leverage a variety of components such as sliders, dropdowns, plots, indicators, tables and layouts to craft a visually stunning and functional application.
By following along, you’ll gain insights into how these components can be seamlessly combined to present data in a meaningful way.
Our objective is to empower you to replicate this process for your own datasets and use cases, enabling you to create impactful dashboards tailored to your specific needs.
Click the dropdowns below to see the requirements or the full code.
Requirements
pip install hvplot pandas panel
conda install -y -c conda-forge hvplot pandas panel
Code
import hvplot.pandas
import pandas as pd
import panel as pn
pn.extension("tabulator", sizing_mode="stretch_width", throttled=True)
ACCENT = "#008080" # teal
styles = {
"box-shadow": "rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px",
"border-radius": "4px",
"padding": "10px",
}
# Extract Data
@pn.cache # only download data once
def get_data():
return pd.read_csv("https://assets.holoviz.org/panel/tutorials/turbines.csv.gz")
source_data = get_data()
# Transform Data
min_year = int(source_data["p_year"].min())
max_year = int(source_data["p_year"].max())
top_manufacturers = (
source_data.groupby("t_manu").p_cap.sum().sort_values().iloc[-10:].index.to_list()
)
def filter_data(t_manu, year):
data = source_data[(source_data.t_manu == t_manu) & (source_data.p_year <= year)]
return data
# Filters
t_manu = pn.ui.Select(
label="Manufacturer",
value="Vestas",
options=sorted(top_manufacturers),
description="The name of the manufacturer",
)
p_year = pn.ui.IntSlider(
label="Year", value=max_year, start=min_year, end=max_year, format="0"
)
# Transform Data 2
df = pn.rx(filter_data)(t_manu=t_manu, year=p_year)
count = df.rx.len()
total_capacity = df.t_cap.sum()
avg_capacity = df.t_cap.mean()
avg_rotor_diameter = df.t_rd.mean()
# Plot Data
fig = (
df[["p_year", "t_cap"]].groupby("p_year").sum() / 10**6
).hvplot.bar(
title="Capacity Change",
rot=90,
ylabel="Capacity (GW)",
xlabel="Year",
xlim=(min_year, max_year),
color=ACCENT,
responsive=True,
)
# Display Data
image = pn.ui.PNG("https://assets.holoviz.org/panel/tutorials/wind_turbines_sunset.png")
indicators = pn.ui.FlexBox(
pn.ui.Number(
value=count,
label="Count",
format="{value:,.0f}",
font_size="28pt",
styles=styles,
),
pn.ui.Number(
value=total_capacity / 1e6,
label="Total Capacity (GW)",
format="{value:,.1f}",
font_size="28pt",
styles=styles,
),
pn.ui.Number(
value=avg_capacity/1e3,
label="Avg. Capacity (MW)",
format="{value:,.1f}",
font_size="28pt",
styles=styles,
),
pn.ui.Number(
value=avg_rotor_diameter,
label="Avg. Rotor Diameter (m)",
format="{value:,.1f}",
font_size="28pt",
styles=styles,
),
)
plot = pn.ui.HoloViews(fig, sizing_mode="stretch_both")
table = pn.ui.Tabulator(df, sizing_mode="stretch_both")
# Layout Data
tabs = pn.ui.Tabs(("Plot", plot), ("Table", table), styles=styles, height=500, margin=10)
pn.ui.Page(
title="Wind Turbine Dashboard",
sidebar=[image, t_manu, p_year],
main=[indicators, tabs],
theme_config={"palette": {"primary": {"main": ACCENT}}},
).servable()
Explanation#
Importing Libraries#
import hvplot.pandas
import pandas as pd
import panel as pn
Extension#
pn.extension("tabulator", sizing_mode="stretch_width", throttled=True)
pn.extension("tabulator", ...): This line loads the JavaScript for the Tabulator widget, which provides a high-performance interactive table.sizing_mode="stretch_width"makes components fill the available width by default, andthrottled=Truemakes the year slider only trigger updates when it is released rather than on every step of a drag.
Styles#
ACCENT = "#008080" # teal
styles = {
"box-shadow": "rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px",
"border-radius": "4px",
"padding": "10px",
}
ACCENT: This variable stores the hex code of the color teal, which will be used as the accent color throughout the dashboard. The theme of thePagerequires a hex (orrgb(...)) color rather than a CSS color name.styles: This dictionary contains CSS styles to be applied to various components in the dashboard for consistent styling, including box shadow, border radius, and padding.
Data Extraction#
@pn.cache
def get_data():
return pd.read_csv("https://assets.holoviz.org/panel/tutorials/turbines.csv.gz")
source_data = get_data()
get_data(): This function is decorated with@pn.cache, which ensures that the data is only downloaded once, improving performance by caching the result.source_data: This variable stores the downloaded CSV data as a Pandas DataFrame.
Data Transformation#
min_year = int(source_data["p_year"].min())
max_year = int(source_data["p_year"].max())
top_manufacturers = (
source_data.groupby("t_manu").p_cap.sum().sort_values().iloc[-10:].index.to_list()
)
def filter_data(t_manu, year):
data = source_data[(source_data.t_manu == t_manu) & (source_data.p_year <= year)]
return data
min_year,max_year: These variables store the minimum and maximum values of the “p_year” column in the DataFrame, representing the earliest and latest years.top_manufacturers: This variable stores the top 10 manufacturers based on the sum of their turbine capacities.filter_data(): This function filters the source data based on the selected manufacturer and year.
Widgets#
t_manu = pn.ui.Select(
label="Manufacturer",
value="Vestas",
options=sorted(top_manufacturers),
description="The name of the manufacturer",
)
p_year = pn.ui.IntSlider(
label="Year", value=max_year, start=min_year, end=max_year, format="0"
)
t_manu: This widget is a select dropdown for choosing the manufacturer.p_year: This widget is an integer slider for selecting the year.format="0"displays the year without a thousands separator.
Data Transformation 2#
df = pn.rx(filter_data)(t_manu=t_manu, year=p_year)
count = df.rx.len()
total_capacity = df.t_cap.sum()
avg_capacity = df.t_cap.mean()
avg_rotor_diameter = df.t_rd.mean()
df: This variable stores the filtered DataFrame based on the selected manufacturer and year.count,total_capacity,avg_capacity,avg_rotor_diameter: These variables calculate various statistics from the filtered DataFrame, such as count, total capacity, average capacity, and average rotor diameter.
Plotting Data#
fig = (
df[["p_year", "t_cap"]].groupby("p_year").sum() / 10**6
).hvplot.bar(
title="Capacity Change",
rot=90,
ylabel="Capacity (GW)",
xlabel="Year",
xlim=(min_year, max_year),
color=ACCENT,
responsive=True,
)
fig: This variable stores a bar plot of the total turbine capacity over the years.responsive=Truemakes the plot fill the space of the tab it is displayed in.
Displaying Data#
image = pn.ui.PNG("https://assets.holoviz.org/panel/tutorials/wind_turbines_sunset.png")
indicators = pn.ui.FlexBox(
pn.ui.Number(
value=count,
label="Count",
format="{value:,.0f}",
font_size="28pt",
styles=styles,
),
pn.ui.Number(
value=total_capacity / 1e6,
label="Total Capacity (GW)",
format="{value:,.1f}",
font_size="28pt",
styles=styles,
),
pn.ui.Number(
value=avg_capacity/1e3,
label="Avg. Capacity (MW)",
format="{value:,.1f}",
font_size="28pt",
styles=styles,
),
pn.ui.Number(
value=avg_rotor_diameter,
label="Avg. Rotor Diameter (m)",
format="{value:,.1f}",
font_size="28pt",
styles=styles,
),
)
image: This variable stores an image pane displaying a background image for the dashboard.indicators: This variable stores a collection ofNumberindicators displaying various statistics. We reduce theirfont_sizefrom the default of 54pt so all four fit side by side.
Creating Components#
plot = pn.ui.HoloViews(fig, sizing_mode="stretch_both")
table = pn.ui.Tabulator(df, sizing_mode="stretch_both")
plot: This variable stores a HoloViews plot pane displaying the bar plot.table: This variable stores a Tabulator widget displaying the filtered DataFrame as a table.
Layout#
tabs = pn.ui.Tabs(("Plot", plot), ("Table", table), styles=styles, height=500, margin=10)
tabs: This variable stores a Tabs layout containing the plot and table, allowing users to switch between them. Each(title, component)tuple sets the title of a tab.
Creating the Dashboard#
pn.ui.Page(
title="Wind Turbine Dashboard",
sidebar=[image, t_manu, p_year],
main=[indicators, tabs],
theme_config={"palette": {"primary": {"main": ACCENT}}},
).servable()
This code creates a
Pagewith a sidebar containing the image, manufacturer select dropdown, and year slider, and a main section containing the indicators and tabs. Thetheme_configsets the primary color of the theme to ourACCENTcolor.
Conclusion#
This code sets up a dynamic dashboard for exploring wind turbine data, including filtering, data transformation, visualization, and statistics display. It leverages various Panel widgets, HoloViews plots, and CSS styling to create an interactive and informative dashboard.