How to build a simple time series dashboard in Python with Panel, Altair and a Jupyter Notebook | by Ben Dexter Cooley | Towards Data Science

I’ve been using Altair for over a year now, and it has quickly become my go-to charting library in Python. I love the built-in interactivity of the plots and the fact that the syntax is built on the Grammar of Graphics.
Altair even has some built-in interactivity through using Vega widgets. However, I have found this to be limiting at times and it doesn’t really allow me to create layouts the way I would want to for a dashboard.
Then I found Panel. Panel calls itself a “high-level app and dashboarding solution for Python” and it’s part of the HoloViz ecosystem managed by Anaconda. I’d heard of HoloViz before (and it’s relative overview site, PyViz), but never really spent the time to dive into the landscape. So here we go!
At first glance, what I love about Panel is that it is plotting-library-agnostic — it supports nearly all visualization libraries. So you don’t have to be a loyal Altair user to learn a bit about making dashboards in this post. That being said, compared to the other code samples in the Panel example gallery, I think the integration with Altair feels really intuitive.
Here are a few other really nice things about Panel:
It’s reactive (updates automatically!)
It’s declarative (readable code)
Supports different layouts (flexible)
Fully deployable to a server (shareable)
Jupyter Notebook compatible (but not dependent...Altair, however, is dependent on Jupyter. So I don’t advise trying this tutorial in something else.)
Here’s what we are going to build: the simplest of little dashboards, composed of an area chart and two filters. We’ll also add a title and subtitle for good measure. All within a Jupyter Notebook.
This tutorial will break the code into chunks and walk through it bit-by-bit, but if you just want dive into the full code (with comments), the Github repo is here.
Now for the code!
First, as always, import those dependent libraries. Here’s what you need:
import panel as pnimport altair as altfrom altair import datumimport pandas as pdfrom vega_datasets import dataimport datetime as dt
Updated August 27: please make sure that your Altair package is version 3.2 or above, otherwise you’ll get some data formatting errors.
Then we need to add two special lines of code, one for Altair and one for Panel. The first tells Altair to set the Vega-Lite rendered to Jupyter Notebook (if you’re using Jupyter Lab, check the Altair docs for alternative). The second line tells Panel to accept Vega (which powers Altair) as an extension. You can learn more about how extensions work in the components section of the Panel docs.
alt.renderers.enable(‘default’)pn.extension(‘vega’)
Since we’re using some sample data from the vega_datasets package, let’s preview our dataframe.
Now the fun part: let’s make some widgets! We’ll be making a dropdown and a date range slider to filter our data.
The dropdown widget takes two parameters: a title for your widget and the “options”.
# create list of company names (tickers) to use as optionstickers = [‘AAPL’, ‘GOOG’, ‘IBM’, ‘MSFT’]# this creates the dropdown widgetticker = pn.widgets.Select(name=’Company’, options=tickers)
Then we’ll create the date range slider. You can access this using the same pn.widgets method. The range slider takes four parameters: start date, end date, default starting date and default ending date.
# this creates the date range sliderdate_range_slider = pn.widgets.DateRangeSlider(name=’Date Range Slider’,start=dt.datetime(2001, 1, 1), end=dt.datetime(2010, 1, 1),value=(dt.datetime(2001, 1, 1), dt.datetime(2010, 1, 1)))
Widgets done! Now let’s add a title and subtitle, so it’s clear to someone else what this dashboard is about. Panel uses Markdown so it’s easy to specify headings.
title = ‘### Stock Price Dashboard’subtitle = ‘This dashboard allows you to select a company and date range to see stock prices.’
Note that we are just declaring variables at this point. Nothing has been built. But now, we start to get into the dashboard-building stuff.
To create a reactive dashboard, we need to tell our Panel object what to “depend” on. This effectively tells Panel to listen for changes in our widgets, and then reload the chart. This line will act as a decorator for the function: the ticker.param.value and date_range_slider.param.value will be used within our get_plot() function, specifically the Altair bit to manipulate the chart.
@pn.depends(ticker.param.value, date_range_slider.param.value)
We’re reactive. Now it’s time to create the plot directly below this line. Let’s write a function that does all our plotting dirty work. This will contain all the data shaping/manipulating as well as the code that creates out Altair chart. We will split this code into three parts using comments: 1) format data, 2) create pandas filters and 3) create Altair object.
def get_plot(ticker, date_range):     # Load and format the data     df = source # define df     df[‘date’] = pd.to_datetime(df[‘date’])     # create date filter using values from the range slider     # store the first and last date range slider value in a var     start_date = date_range_slider.value[0]      end_date = date_range_slider.value[1]      # create filter mask for the dataframe     mask = (df[‘date’] > start_date) & (df[‘date’] <= end_date)     df = df.loc[mask] # filter the dataframe     # create the Altair chart object     chart = alt.Chart(df).mark_line().encode(x=’date’, y=‘price’,      tooltip=alt.Tooltip([‘date’,’price’])).transform_filter((datum.symbol == ticker) # this ties in the filter )     return chart
Almost there! Now we need to create our final Panel object. Panel objects can consist of rows and columns. Since this is a simple little dashboard, we will just use two columns.
First we create our single row. Then, we fill it with the contents of two columns. Our first column will contain our 1) title, 2) subtitle, 3) dropdown and 4) date slider. The second column will display the chart.
dashboard = pn.Row(pn.Column(title, subtitle, ticker, date_range_slider),get_plot # our draw chart function!)
And we’re done! Simply call your dashboard variable and see your tiny little app in all of its beauty.
Another cool thing about Panel is it’s ability to deploy apps through a Bokeh server. For now, we’ll simply take our dashboard and add it as a “servable” local app for Bokeh so we can test our dashboard functionality. For all the details on deploying, check out the extensive deploy and export Panel page and the Bokeh docs on running a Bokeh server.
Adding this line of code:
dashboard.servable()
Will make your dashboard discoverable to Bokeh server. Now we’ll need to pop over to the command line to start our server. Run the below code to start your server as a localhost. The “ — show” command just tells Bokeh to pop open a new tab in the browser with your app displayed as soon as the server is ready. You can copy/paste this line into the terminal:
panel serve --show panel-altair-demo.ipynb
And there it is! Our little stock price app. Of course, this is using a standard, demo dataset. But hopefully you can start to see how you can plug in your dataset and create a more useful application. Think of the three columns of the dataframe simply as placeholders:
symbol → <your categorical variable>
date → <your dates and/or times>
price → <your values>
Even better, you can create a dashboard with multiple charts included. All without leaving the comfort of you cozy Jupyter Notebook.
I’m pumped to dive into Panel in more detail to start making more complex dashboards, quick prototypes, internal tools, etc. Happy coding!
The Panel documentation has a fantastic gallery of examples to get started with, so head on over to see what else is possible. This tutorial relies heavily on the Altair example, but I have adapted the code to what I feel is a cleaner version which should help for adapting to different datasets and chart types. I also added the date range slider as a new widget. ICYMI full commented code is in this repo.