Exercises 1-2: Building a Dashboard
import numpy as np
import panel as pn
import holoviews as hv
from holoviews import opts # noqa
pn.extension("katex")
hv.extension("bokeh")
# Run this if you haven't already to fetch earthquake and population data files
from fetch_data import *
get_earthquake_data()
get_population_data()
Earthquakes dataset present, skipping download
Exercise 1#
In this exercise you will construct a number of Panel components and then lay them out as a non-interactive Panel dashboard.
The data#
Throughout this tutorial we will be working with one core dataset, a collection of earthquakes recorded between 2000-2018 provided by the US Geological Survey (USGS). The data is provided as a Parquet file as part of the tutorial and we will load it using Dask and persist it. We will return to this later; for now we will focus on building a dashboard and you don’t know any of the details about the dataset or the Dask or Pandas API.
import dask.dataframe as dd
df = dd.read_parquet("../../data/earthquakes.parq", index='index')
df.time = df.time.astype("datetime64[ns]")
df = df[~df.mag.isna()].persist()
The logo#
The first component of the dashboard is an image of the US Geological Survey
logo. Start by declaring a pane containing the logo and assign it to the logo
variable. Also set a width to ensure the logo doesn’t appear too big.
logo_url = "../../assets/usgs_logo.png"
## Define a panel component containing the logo
logo = ...
## Display it
Solution
logo = pn.panel(logo_url, width=300)
Richter scale equation#
Next we will create a component to display the equation for the Richter scale
definition. Declare the appropriate pane and assign it to the equation
variable.
equation_string = "$M_L = log_{10}A - log_{10} A_0(\delta)$"
## Define a panel component containing the equation (Hint: Use the LaTeX pane)
equation = ...
## Display it
Solution
equation = pn.pane.LaTeX(equation_string)
List the strongest earthquakes#
year = 2000
def strongest_earthquakes_fn(year):
year_df = df[(df.time.dt.year == year) & (df.mag > 7)].compute()
return (
year_df.sort_values("mag", ascending=False)
.iloc[:5][["time", "place", "mag"]]
.reset_index(drop=True)
)
## Create a panel component by calling the function with a particular year
strongest_earthquakes = ...
## Display it
Solution
strongest_earthquakes = pn.panel(strongest_earthquakes_fn(year))
Display an iframe of a Google Map#
Hint
An iframe is an HTML tag.
def gmap_fn(year):
yearly_df = df[(df.time.dt.year == year)].compute()
index = np.argmax(yearly_df.mag.values)
strongest = yearly_df.iloc[index]
lon, lat = strongest.longitude, strongest.latitude
return """
<iframe width="300" height="300" src="https://maps.google.com/maps?q={lat},{lon}&z=6&output=embed"
frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>
""".format(
lat=lat, lon=lon
)
## Create a panel component by calling the function with a particular year and wrapping it in the appropriate pane
gmap = ...
## Display it
A plot [challenge]#
If you are up to it, create a custom plot from the year_df
dataframe defined
below, create a Panel component, and assign it to the plot
variable.
Info
If you are using matplotlib pyplot you can get the figure with
plot = plt.gcf()
and the close it with plot.close()
year_df = df[df.time.dt.year == year].compute()
## Create a plot and assign it to the plot variable
plot = ...
## Display it
Solution
This example solution uses concepts covered in the plotting section of the tutorial:
plot = hv.Violin(year_df, 'type', 'mag').opts(ylim=(0, None), xrotation=90)
Composing the dashboard#
Now that we have defined all the different components, it is time to lay them out into the overall dashboard.
Arrange the following components into a dashboard using the Row
and Column
panels:
logo
equation
strongest_earthquakes
gmap
plot
(optional)
Solution
year = 2000
logo = pn.panel(logo_url, width=200)
equation = pn.pane.LaTeX(equation_string)
strongest_earthquakes = strongest_earthquakes_fn(year)
gmap = pn.pane.HTML(gmap_fn(year), height=300, width=300)
plot = hv.Violin(year_df, 'type', ('mag', 'Magnitude')).opts(ylim=(0, None))
title = pn.panel('# Strongest Earthquakes in the Year %d' % year, width=400)
header = pn.Row(title, pn.layout.HSpacer(), logo)
body = pn.Row(
pn.Column('### Strongest Earthquakes', strongest_earthquakes),
pn.Column('### Description', gmap),
pn.Column('### Magnitude Plot', plot)
)
pn.Column(header, body)
Exercise 2#
Having learned about how to create interactive components we can now make the formerly static dashboard interactive by adding a widget to control the year.
The widget#
Declare an IntSlider
widget with a start value of 2000, end value of 2018, and
current value of 2000.
Solution
year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)
year_slider
The title#
Write a function with dependencies which returns a title for the given year, e.g. “Strongest Earthquakes in the Year 2000”:
Solution
@pn.depends(year_slider.param.value)
def title_fn(year):
return '## Strongest Earthquakes in the Year {year}'.format(year=year)
The table and map#
Add dependencies to the functions below so the output updates whenever the slider value changes:
def strongest_earthquakes_fn(year): # noqa: redefined on purpose
year_df = df[df.time.dt.year == year].compute()
return (
year_df.sort_values("mag", ascending=False)
.iloc[:5][["time", "place", "mag"]]
.reset_index(drop=True)
)
def gmap_fn(year): # noqa: redefined on purpose
yearly_df = df[(df.time.dt.year == year)].compute()
index = np.argmax(yearly_df.mag.values)
strongest = yearly_df.iloc[index]
lon, lat = strongest.longitude, strongest.latitude
return pn.pane.HTML(
"""
<iframe width="300" height="300" src="https://maps.google.com/maps?q={lat},{lon}&z=6&output=embed"
frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>
""".format(
lat=lat, lon=lon
),
height=300,
width=300,
)
The Plot [challenge]#
In case you defined a plot above make the plot dynamic by wrapping it in a function which depends on the year.
Solution
@pn.depends(year_slider.param.value)
def plot_fn(year):
year_df = df[df.time.dt.year == year].compute()
return hv.Violin(year_df, 'type', ('mag', 'Magnitude')).opts(xrotation=90)
Composing the dashboard#
Now that we have created new dynamic components let us lay them out once again to create a fully interactive dashboard. Ensure that you include the widget so you can actually control the year.
Solution
year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)
@pn.depends(year_slider.param.value)
def title_fn(year):
return '## Strongest Earthquakes in the Year {year}'.format(year=year)
@pn.depends(year_slider.param.value)
def strongest_earthquakes_fn(year):
year_df = df[df.time.dt.year == year].compute()
return year_df.sort_values('mag', ascending=False).iloc[:5][['time', 'place', 'mag']].reset_index(drop=True)
@pn.depends(year_slider.param.value)
def gmap_fn(year):
yearly_df = df[(df.time.dt.year == year)].compute()
index = np.argmax(yearly_df.mag.values)
strongest = yearly_df.iloc[index]
lon, lat = strongest.longitude, strongest.latitude
return pn.pane.HTML("""
<iframe width="300" height="300" src="https://maps.google.com/maps?q={lat},{lon}&z=6&output=embed"
frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>
""".format(lat=lat, lon=lon), height=300, width=300)
@pn.depends(year_slider.param.value)
def plot_fn(year):
year_df = df[df.time.dt.year == year].compute()
return hv.Violin(year_df, 'type', ('mag', 'Magnitude'))
logo = pn.panel(logo_url, width=200)
equation = pn.pane.LaTeX(equation_string)
header = pn.Row(title_fn, pn.layout.HSpacer(), logo)
body = pn.Row(
pn.Column('### Strongest Earthquakes', strongest_earthquakes_fn),
pn.Column('### Map', gmap_fn),
pn.Column('### Magnitude Plot', plot_fn)
)
pn.Column(header, year_slider, body)