Building a Dashboard#

import pathlib
import numpy as np
import pandas as pd
import panel as pn
pn.extension('katex')

import hvplot.pandas # noqa

Non-interactive components#

In this exercise you will first construct a number of Panel components and then lay them out as a non-interactive Panel dashboard, using the usual earthquakes dataset.

%%time
df = pd.read_parquet(pathlib.Path('../../data/earthquakes-projected.parq'))
columns = ['mag', 'depth', 'latitude', 'longitude', 'place', 'type']
df = df[columns]
most_severe = df[df.mag >= 7]
CPU times: user 2.13 s, sys: 424 ms, total: 2.56 s
Wall time: 1.73 s

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
equation = pn.pane.LaTeX(equation_string)
equation

List the strongest earthquakes#

year = 2000

def strongest_earthquakes_fn(year):
    year_df = df[(df.index.year == year) & (df.mag > 7)]
    return year_df.sort_values('mag', ascending=False).iloc[:5][['place', 'mag']].reset_index()

## Create a panel component by calling the function with a particular year
strongest_earthquakes = ...

## Display it
strongest_earthquakes = pn.panel(strongest_earthquakes_fn(year))
strongest_earthquakes

Display an iframe of a Google Map#

Hint

An iframe is an HTML tag.

def gmap_fn(year):
    yearly_df = df[(df.index.year == year)]
    index = np.argmax(yearly_df.mag.fillna(0).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

This example solution displays the map for the year (by default is the year 2000):

gmap = pn.panel(gmap_fn(year))
gmap

A plot#

Here we create a plot from the year_df dataframe with .hvplot, create a Panel component, and assign it to the plot variable. We can visualize the distribution of earthquake magnitudes over the year by plotting the time variable on the x-axis and the mag variable on the y-axis.

year_df = df[df.index.year == year]

## Create a plot and assign it to the plot variable
plot = ...

## Display it

This example solution uses concepts covered in the plotting section of the tutorial:

plot = year_df.hvplot.line(x='time', y='mag')
plot

Composing the static 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

Hint

Remember that you can nest panel Row and Column layouts and use HSpacer and VSpacer objects to adjust spacing as necessary.

# Static Dashboard combining all the elements above.
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)
year_df = df[(df.index.year == year) & (df.mag > 7)]
plot = year_df.hvplot.line(x='time', y='mag')

title = pn.panel('# Strongest Earthquakes in the Year %d' % year, width=400)
header = pn.Row(title, pn.layout.HSpacer(), logo)

body_row1 = pn.Row(
    pn.Column('### Richter scale definition', equation, '### Strongest Earthquake', gmap),
    pn.Column('### Strongest Earthquakes', strongest_earthquakes),
)

pn.Column(header, pn.Column(body_row1,pn.Column('### Magnitude Plot', plot)))

A dashboard with interactive widgets#

Having learned about how to create interactive components, we can now integrate them into dashboards. Here is one of the interactive plots from the last exercise:

depth_slider = pn.widgets.FloatSlider(name='Minimum depth', start=0, end=700, value=350)

dfi = pn.rx(most_severe)
depth_filtered = dfi[dfi['depth'] < depth_slider]
depth_filtered_plot = depth_filtered.hvplot(y='mag', kind='scatter', color='red', marker='x')
#depth_filtered_plot

Have a look at this object to remind you what it represents.

Adding a title and description#

Using the following markdown description of this plot, create a dashboard that integrates it together with an appropriate title, USGS logo and the interactive plot itself.

plot_description_markdown = """
This plot filters earthquakes according to the minimum depth slider and displays the magnitude of the filtered earthquakes over time. Note that only earthquakes with a magnitude `>7` are shown.
"""
title = pn.panel('# Earthquake magnitude as function of depth')
header = pn.Row(title, pn.layout.HSpacer(), logo)

pn.Column(header, pn.panel(plot_description_markdown), depth_filtered_plot)

A dashboard filtering over year#

Now let us make a dashboard driven by a year slider. Create a integer slider that ranges from year 2000 to 2018 called year_slider.

Hint

You can use a pn.widgets.IntSlider to select the year.

year_slider = ...
# Display the slider
year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)
year_slider

Now let’s look at how the strongest_earthquakes_fn displays a DataFrame for the most severe earthquakes in a given year:

year = 2000
most_severe_2000 = most_severe[most_severe.index.year == year]
most_severe_2000.sort_values('mag', ascending=False).iloc[:5].reset_index()[['time', 'place', 'mag']]
time place mag
0 2000-11-16 04:54:56.740000+00:00 New Ireland region, Papua New Guinea 8.0
1 2000-06-04 16:28:26.170000+00:00 southern Sumatra, Indonesia 7.9
2 2000-06-18 14:44:13.310000+00:00 South Indian Ocean 7.9
3 2000-11-16 07:42:16.930000+00:00 New Ireland region, Papua New Guinea 7.8
4 2000-11-17 21:01:56.490000+00:00 New Britain region, Papua New Guinea 7.8

Using this as an example, create an equivalent table driven by year_slider, by declaring a filtered interactive DataFrame called year_rdf. The handle on the result should be called year_table.

# Display the table above but with a selectable year using year_slider
year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)
rdf = pn.rx(most_severe)
year_rdf = rdf[rdf.index.year == year_slider]
year_table = year_rdf.sort_values('mag', ascending=False).iloc[:5].reset_index()[['time', 'place', 'mag']]
year_table

Now create a scatter plot from year_rdf with red cross (x) markers of size 50 called year_scatter.

Hint

Use a hvplot call with kind='scatter'. You also need to set the color and size keywords and set size=50.

# Create an interactive scatter called year_scatter
year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)
rdf = pn.rx(most_severe)
year_rdf = rdf[rdf.index.year == year_slider]
year_scatter = year_rdf.mag.hvplot(kind='scatter', color='red', marker='x', size=50)
year_scatter

Putting these components together#

Now using the terminal methods on our interactive dataframe together with a panel Markdown title and the Row and Column layout, we can now put year_slider, year_table and year_scatter together into a dashboard.

Hint

Remember that you can use ReactiveExpr with show_widgets=False or pass the expression to another Panel component to display it without the widget. A single year_slider can then be displayed by a pn.Column.

# Build a dashboard using year_slider, year_table and year_scatter
year_rdf = rdf[rdf.index.year == year_slider]

year_table = year_rdf.sort_values('mag', ascending=False).reset_index().iloc[:5][['time', 'place', 'mag']]
year_scatter = year_rdf.mag.hvplot(kind='scatter', color='red', marker='x', size=50)
pn.Column('# Earthquake magnitudes by year ',
          year_slider,
          pn.Row(pn.panel(year_table,   show_widgets=False),
                 pn.panel(year_scatter, show_widgets=False))).servable()
This web page was generated from a Jupyter notebook and not all interactivity will work on this website. Right click to download and run locally for full Python-backed interactivity.

Right click to download this notebook from GitHub.