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

Exercise 4: Building a Dashboard

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pathlib\n", "import numpy as np\n", "import pandas as pd\n", "import panel as pn\n", "pn.extension('katex')\n", "\n", "import hvplot.pandas # noqa" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Non-interactive components\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "df = pd.read_parquet(pathlib.Path('../../data/earthquakes-projected.parq'))\n", "columns = ['mag', 'depth', 'latitude', 'longitude', 'place', 'type']\n", "df = df[columns]\n", "most_severe = df[df.mag >= 7]" ] }, { "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 = pathlib.Path('../../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", "logo\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", "equation = pn.pane.LaTeX(equation_string)\n", "equation\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.index.year == year) & (df.mag > 7)]\n", " return year_df.sort_values('mag', ascending=False).iloc[:5][['place', 'mag']].reset_index()\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", "strongest_earthquakes\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.index.year == year)]\n", " index = np.argmax(yearly_df.mag.fillna(0).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": [ "
Solution
\n", "\n", "This example solution displays the map for the `year` (by default is the year 2000):\n", "\n", "```python\n", "gmap = pn.panel(gmap_fn(year))\n", "gmap\n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A plot \n", "\n", "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.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "year_df = df[df.index.year == year]\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 = year_df.hvplot.line(x='time', y='mag')\n", "plot\n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Composing the static 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``\n", "\n", "\n", "
Hint\n", "\n", "Remember that you can nest panel `Row` and `Column` layouts and use `HSpacer` and `VSpacer` objects to adjust spacing as necessary.\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Static Dashboard combining all the elements above." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Example 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", "year_df = df[(df.index.year == year) & (df.mag > 7)]\n", "plot = year_df.hvplot.line(x='time', y='mag')\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_row1 = pn.Row(\n", " pn.Column('### Richter scale definition', equation, '### Strongest Earthquake', gmap),\n", " pn.Column('### Strongest Earthquakes', strongest_earthquakes),\n", ")\n", "\n", "pn.Column(header, pn.Column(body_row1,pn.Column('### Magnitude Plot', plot)))\n", "```\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A dashboard with interactive widgets\n", "\n", "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:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "depth_slider = pn.widgets.FloatSlider(name='Minimum depth', start=0, end=700, value=350)\n", "\n", "dfi = pn.rx(most_severe)\n", "depth_filtered = dfi[dfi['depth'] < depth_slider]\n", "depth_filtered_plot = depth_filtered.hvplot(y='mag', kind='scatter', color='red', marker='x')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#depth_filtered_plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Have a look at this object to remind you what it represents.\n", "\n", "#### Adding a title and description\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_description_markdown = \"\"\"\n", "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.\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "title = pn.panel('# Earthquake magnitude as function of depth')\n", "header = pn.Row(title, pn.layout.HSpacer(), logo)\n", "\n", "pn.Column(header, pn.panel(plot_description_markdown), depth_filtered_plot)\n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A dashboard filtering over year\n", "\n", "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`.\n", "\n", "
Hint\n", "\n", "You can use a `pn.widgets.IntSlider` to select the year.\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "year_slider = ...\n", "# Display the slider" ] }, { "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": [ "Now let's look at how the `strongest_earthquakes_fn` displays a `DataFrame` for the most severe earthquakes in a given year:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "year = 2000\n", "most_severe_2000 = most_severe[most_severe.index.year == year]\n", "most_severe_2000.sort_values('mag', ascending=False).iloc[:5].reset_index()[['time', 'place', 'mag']]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Display the table above but with a selectable year using year_slider" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)\n", "rdf = pn.rx(most_severe)\n", "year_rdf = rdf[rdf.index.year == year_slider]\n", "year_table = year_rdf.sort_values('mag', ascending=False).iloc[:5].reset_index()[['time', 'place', 'mag']]\n", "year_table\n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now create a scatter plot from `year_rdf` with red cross (`x`) markers of size `50` called `year_scatter`.\n", "\n", "
\n", " Hint\n", "\n", "Use a `hvplot` call with `kind='scatter'`. You also need to set the `color` and `size` keywords and set `size=50`.\n", "\n", "
\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create an interactive scatter called year_scatter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "year_slider = pn.widgets.IntSlider(name='Year', start=2000, end=2018, value=2000)\n", "rdf = pn.rx(most_severe)\n", "year_rdf = rdf[rdf.index.year == year_slider]\n", "year_scatter = year_rdf.mag.hvplot(kind='scatter', color='red', marker='x', size=50)\n", "year_scatter\n", "```\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Putting these components together\n", "\n", "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.\n", "\n", "\n", "
\n", "Hint\n", "\n", "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`. \n", "\n", "
\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Build a dashboard using year_slider, year_table and year_scatter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Solution
\n", "\n", "```python\n", "year_rdf = rdf[rdf.index.year == year_slider]\n", "\n", "year_table = year_rdf.sort_values('mag', ascending=False).reset_index().iloc[:5][['time', 'place', 'mag']]\n", "year_scatter = year_rdf.mag.hvplot(kind='scatter', color='red', marker='x', size=50)\n", "pn.Column('# Earthquake magnitudes by year ',\n", " year_slider,\n", " pn.Row(pn.panel(year_table, show_widgets=False),\n", " pn.panel(year_scatter, show_widgets=False))).servable()\n", "```\n", "
" ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }