{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\"HoloViz\n", "

Exercises 1-2: Building a Dashboard

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import panel as pn\n", "import holoviews as hv\n", "from holoviews import opts # noqa\n", "\n", "pn.extension('katex')\n", "hv.extension('bokeh')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise 1\n", "\n", "In this exercise you will construct a number of Panel components and then lay them out as a non-interactive Panel dashboard." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The data\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import dask.dataframe as dd\n", "\n", "df = dd.read_parquet('../../data/earthquakes.parq')\n", "\n", "df = df[~df.mag.isna()].persist()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The logo\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "logo_url = '../../assets/usgs_logo.png'\n", "\n", "## Define a panel component containing the logo\n", "logo = ...\n", "\n", "## Display it\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "logo = pn.panel(logo_url, width=300) \n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Richter scale equation\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "equation_string = '$M_L = log_{10}A - log_{10} A_0(\\delta)$'\n", "\n", "## Define a panel component containing the equation (Hint: Use the LaTeX pane)\n", "equation = ...\n", "\n", "## Display it\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "logo = pn.pane.LaTeX(equation_string) \n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### List the strongest earthquakes" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "year = 2000\n", "\n", "def strongest_earthquakes_fn(year):\n", " year_df = df[(df.time.dt.year == year) & (df.mag > 7)].compute()\n", " return year_df.sort_values('mag', ascending=False).iloc[:5][['time', 'place', 'mag']].reset_index(drop=True)\n", "\n", "## Create a panel component by calling the function with a particular year\n", "strongest_earthquakes = ...\n", "\n", "## Display it\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "strongest_earthquakes = pn.panel(strongest_earthquakes_fn(year)) \n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Display an iframe of a Google Map" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Hint
\n", "\n", "An iframe is an HTML tag.\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def gmap_fn(year):\n", " yearly_df = df[(df.time.dt.year == year)].compute()\n", " index = np.argmax(yearly_df.mag.values)\n", " strongest = yearly_df.iloc[index]\n", " lon, lat = strongest.longitude, strongest.latitude\n", " return \"\"\"\n", " \n", " \"\"\".format(lat=lat, lon=lon)\n", "\n", "## Create a panel component by calling the function with a particular year and wrapping it in the appropriate pane\n", "gmap = ...\n", "\n", "## Display it\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A plot [challenge]\n", "\n", "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.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Info
\n", "\n", "If you are using matplotlib pyplot you can get the figure with ``plot = plt.gcf()`` and the close it with ``plot.close()``\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "year_df = df[df.time.dt.year == year].compute()\n", "\n", "## Create a plot and assign it to the plot variable\n", "plot = ...\n", "\n", "## Display it\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "This example solution uses concepts covered in the plotting section of the tutorial:\n", "\n", "```python\n", "plot = hv.Violin(year_df, 'type', 'mag').opts(ylim=(0, None), xrotation=90)\n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Composing the dashboard\n", "\n", "Now that we have defined all the different components, it is time to lay them out into the overall dashboard.\n", "\n", "Arrange the following components into a dashboard using the ``Row`` and ``Column`` panels:\n", "\n", "* ``logo``\n", "* ``equation``\n", "* ``strongest_earthquakes``\n", "* ``gmap``\n", "* ``plot`` (optional)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "year = 2000\n", "\n", "logo = pn.panel(logo_url, width=200)\n", "equation = pn.pane.LaTeX(equation_string)\n", "strongest_earthquakes = strongest_earthquakes_fn(year)\n", "gmap = pn.pane.HTML(gmap_fn(year), height=300, width=300)\n", "plot = hv.Violin(year_df, 'type', ('mag', 'Magnitude')).opts(ylim=(0, None))\n", "\n", "title = pn.panel('# Strongest Earthquakes in the Year %d' % year, width=400)\n", "header = pn.Row(title, pn.layout.HSpacer(), logo)\n", "\n", "body = pn.Row(\n", " pn.Column('### Strongest Earthquakes', strongest_earthquakes),\n", " pn.Column('### Description', gmap),\n", " pn.Column('### Magnitude Plot', plot)\n", ")\n", "\n", "pn.Column(header, body)\n", "```\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise 2\n", "\n", "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The widget\n", "\n", "Declare an ``IntSlider`` widget with a start value of 2000, end value of 2018, and current value of 2000." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)\n", "year_slider\n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "#### The title\n", "\n", "Write a function with dependencies which returns a title for the given year, e.g. \"Strongest Earthquakes in the Year 2000\":" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "@pn.depends(year_slider.param.value)\n", "def title_fn(year):\n", " return '## Strongest Earthquakes in the Year {year}'.format(year=year)\n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The table and map\n", "\n", "Add dependencies to the functions below so the output updates whenever the slider value changes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "def strongest_earthquakes_fn(year): # noqa: redefined on purpose\n", " year_df = df[df.time.dt.year == year].compute()\n", " return year_df.sort_values('mag', ascending=False).iloc[:5][['time', 'place', 'mag']].reset_index(drop=True)\n", "\n", "\n", "def gmap_fn(year): # noqa: redefined on purpose\n", " yearly_df = df[(df.time.dt.year == year)].compute()\n", " index = np.argmax(yearly_df.mag.values)\n", " strongest = yearly_df.iloc[index]\n", " lon, lat = strongest.longitude, strongest.latitude\n", " return pn.pane.HTML(\"\"\"\n", " \n", " \"\"\".format(lat=lat, lon=lon), height=300, width=300)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The Plot [challenge]\n", "\n", "In case you defined a plot above make the plot dynamic by wrapping it in a function which depends on the year." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "@pn.depends(year_slider.param.value)\n", "def plot_fn(year)\n", " year_df = df[df.time.dt.year == year].compute()\n", " return hv.Violin(year_df, 'type', ('mag', 'Magnitude')).opts(xrotation=90)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Composing the dashboard\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)\n", "\n", "@pn.depends(year_slider.param.value)\n", "def title_fn(year):\n", " return '## Strongest Earthquakes in the Year {year}'.format(year=year)\n", " \n", "@pn.depends(year_slider.param.value)\n", "def strongest_earthquakes_fn(year):\n", " year_df = df[df.time.dt.year == year].compute()\n", " return year_df.sort_values('mag', ascending=False).iloc[:5][['time', 'place', 'mag']].reset_index(drop=True)\n", "\n", "@pn.depends(year_slider.param.value)\n", "def gmap_fn(year):\n", " yearly_df = df[(df.time.dt.year == year)].compute()\n", " index = np.argmax(yearly_df.mag.values)\n", " strongest = yearly_df.iloc[index]\n", " lon, lat = strongest.longitude, strongest.latitude\n", " return pn.pane.HTML(\"\"\"\n", " \n", " \"\"\".format(lat=lat, lon=lon), height=300, width=300)\n", "\n", "@pn.depends(year_slider.param.value)\n", "def plot_fn(year):\n", " year_df = df[df.time.dt.year == year].compute()\n", " return hv.Violin(year_df, 'type', ('mag', 'Magnitude'))\n", " \n", "logo = pn.panel(logo_url, width=200)\n", "equation = pn.pane.LaTeX(equation_string)\n", "\n", "header = pn.Row(title_fn, pn.layout.HSpacer(), logo)\n", "\n", "body = pn.Row(\n", " \n", " pn.Column('### Strongest Earthquakes', strongest_earthquakes_fn),\n", " pn.Column('### Map', gmap_fn),\n", " pn.Column('### Magnitude Plot', plot_fn)\n", ")\n", "\n", "pn.Column(header, year_slider, body)\n", "```\n", "\n", "
" ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }