Dashboards (Bokeh)#

Note

This tutorial is adapted from the tutorials on the official Bokeh website.

https://miro.medium.com/v2/resize:fit:300/0*7A8fKKtwo9L8wpNk

Bokeh is a Python library for creating web apps with interactive data visualizations.

It helps you build beautiful graphics, ranging from simple plots to complex dashboards with streaming (real-time) datasets.

With Bokeh, you can create JavaScript-powered visualizations without writing any JavaScript yourself!

Think of it as matplotlib or seaborn that lets you save your plots as interactive web apps, instead of .png images.

Examples#

Example of dashboards built with Bokeh include Interactive Movies Explorer and others listed on the Bokeh website under Demos.

from bokeh.io import output_notebook

output_notebook()
Loading BokehJS ...
from bokeh.palettes import HighContrast3
from bokeh.plotting import figure, show

fruits = ["Apples", "Pears", "Nectarines", "Plums", "Grapes", "Strawberries"]
years = ["2015", "2016", "2017"]

data = {"fruits": fruits, "2015": [2, 1, 4, 3, 2, 4], "2016": [5, 3, 4, 2, 4, 6], "2017": [3, 2, 4, 4, 5, 3]}

p = figure(x_range=fruits, height=250, title="Fruit Counts by Year", toolbar_location=None, tools="hover", tooltips="$name @fruits: @$name")

p.vbar_stack(years, x="fruits", width=0.9, color=HighContrast3, source=data, legend_label=years)

p.y_range.start = 0
p.x_range.range_padding = 0.1
p.xgrid.grid_line_color = None
p.axis.minor_tick_line_color = None
p.outline_line_color = None
p.legend.location = "top_left"
p.legend.orientation = "horizontal"

show(p)

Installation#

Note

You can run Bokeh on Google Colab without needing to install it on your laptop.

Since Bokeh is already pre-installed in this notebook environment, you are able to use Bokeh right away.

However, if you want to install Bokeh in your local environment or any other environment, you can use pip or conda to install Bokeh.

Installing with pip:

pip install bokeh

Installing with conda:

conda install bokeh

See the Bokeh Installation Guide for more details.

Line Plots#

Another common visualization task is the drawing of line plots. This can be accomplished in Bokeh by calling the p.line(...) glyph method as shown below.

# create a new plot (with a title) using figure
p = figure(width=400, height=400, title="My Line Plot")

# add a line renderer
p.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=2)

show(p) # show the results

In addition to line_width, there are other options such as line_color or line_dash that can be set. Try setting some of the other properties of line and re-running the cell above.

Datetime axes#

It’s often the case that timeseries data is represented by drawing lines. Let’s look at an example using the “glucose” data set, which is available in a Pandas dataframe:

from bokeh.sampledata.glucose import data
data.head()
isig glucose
datetime
2010-03-24 09:51:00 22.59 258
2010-03-24 09:56:00 22.52 260
2010-03-24 10:01:00 22.23 258
2010-03-24 10:06:00 21.56 254
2010-03-24 10:11:00 20.79 246

We’d like to plot a subset of this data, and have a nice datetime axis as well. We can ask Bokeh for a datetime axis by passing x_axis_type="datetime" to the call to figure. This is shown below, as well as configuration of a some other options such as plot dimensions, axis titles, and grid line properies.

from bokeh.sampledata.glucose import data
data.head()

# reduce data size to one week
week = data.loc['2010-10-01':'2010-10-08']

p = figure(x_axis_type="datetime", title="Glocose and Isig", height=350, width=800)
p.xgrid.grid_line_color=None
p.ygrid.grid_line_alpha=0.5
p.xaxis.axis_label = 'Time'
p.yaxis.axis_label = 'Value'

p.line(week.index, week.glucose, color='blue')
p.line(week.index, week.isig, color='red')

show(p)

Maps#

It is often useful to be able to relate datasets with their real-world context. You can plot geographic data just like any other type of data, as in the Texas Unemployment example, but Bokeh also provides several specialized mechanisms for plotting data in geographic coordinates:

WTMS is the most common web standard for tiled map data, i.e. maps supplied as standard-sized image patches from which the overall map can be constructed at a given zoom level. WTMS uses Web Mercator format, measuring distances from Greenwich, England as meters north and meters west, which is easy to compute but does distort the global shape.

First let’s create an empty Bokeh plot covering the USA, with bounds specified in meters:

from bokeh.plotting import figure
from bokeh.models import WMTSTileSource

# web mercator coordinates
USA = x_range,y_range = ((-13884029,-7453304), (2698291,6455972))

p = figure(tools='pan, wheel_zoom', x_range=x_range, y_range=y_range, 
           x_axis_type="mercator", y_axis_type="mercator")

A few WTMS tile sources are already defined in bokeh.tile_providers, but here we’ll show how to specify the interface using a format string showing Bokeh how to request a tile with the required zoom, x, and y values from a given tile provider:

url = 'http://a.basemaps.cartocdn.com/rastertiles/voyager/{Z}/{X}/{Y}.png'
attribution = "Tiles by Carto, under CC BY 3.0. Data by OSM, under ODbL"

p.add_tile(WMTSTileSource(url=url, attribution=attribution))

show(p)
import pandas as pd
import numpy as np

def wgs84_to_web_mercator(df, lon="lon", lat="lat"):
    """Converts decimal longitude/latitude to Web Mercator format"""
    k = 6378137
    df["x"] = df[lon] * (k * np.pi/180.0)
    df["y"] = np.log(np.tan((90 + df[lat]) * np.pi/360.0)) * k
    return df

df = pd.DataFrame(dict(name=["Austin", "NYC"], lon=[-97.7431,-74.0059], lat=[30.2672,40.7128]))
wgs84_to_web_mercator(df)
name lon lat x y
0 Austin -97.7431 30.2672 -1.088071e+07 3.537942e+06
1 NYC -74.0059 40.7128 -8.238299e+06 4.970072e+06
p = figure(tools='pan, wheel_zoom', x_range=x_range, y_range=y_range, 
           x_axis_type="mercator", y_axis_type="mercator")

p.add_tile(WMTSTileSource(url=url, attribution=attribution))

p.circle(x=df['x'], y=df['y'], fill_color='orange', size=10)
show(p)

Widgets#

from datetime import date

from bokeh.io import show
from bokeh.models import CustomJS, DateRangeSlider


date_range_slider = DateRangeSlider(value=(date(2016, 1, 1), date(2016, 12, 31)),
                                    start=date(2015, 1, 1), end=date(2017, 12, 31))
date_range_slider.js_on_change("value", CustomJS(code="""
    console.log('date_range_slider: value=' + this.value, this.toString())
"""))

show(date_range_slider)

Layouts#

Creating dashboard layouts with Bokeh is simple.

Just use row and column functions from bokeh.layouts to arrange plots and widgets in a grid-like fashion.

from bokeh.layouts import column 
from bokeh.models import ColumnDataSource, Slider, CustomJS 
from bokeh.plotting import figure, output_file, show 
import numpy as np 
from bokeh.io import reset_output, output_notebook

reset_output()
output_notebook()

x = np.linspace(0, 10, 500) 
y = np.sin(x) 
  
source = ColumnDataSource(data=dict(x=x, y=y)) 
  
# Create plots and widgets 
plot = figure() 
  
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.5) 
  
# Create Slider object 
slider = Slider(start=0, end=6, value=2, 
                step=0.2, title='Number of points') 
  
# Adding callback code 
callback = CustomJS(args=dict(source=source, val=slider), 
                    code=""" 
    const data = source.data; 
    const freq = val.value; 
    const x = data['x']; 
    const y = data['y']; 
   for (var i = 0; i < x.length; i++) { 
        y[i] = Math.sin(freq*x[i]); 
    } 
    source.change.emit(); 
""") 
  
slider.js_on_change('value', callback) 
  
# Arrange plots and widgets in layouts 
layout = column(slider, plot) 
  
# output_file('exam.html') 
  
show(layout) 
Loading BokehJS ...

Saving to an HTML File#

It is also often useful to generate a standalone HTML script containing Bokeh content. This is accomplished by calling the output_file(...) function. It is especially common to do this from standard Python scripts, but here we see that it works in the notebook as well.

# from bokeh.io import output_file, show

# output_file("plot.html")

# show(p)   # save(p) will save without opening a new browser tab

In addition the inline plot above, you should also have seen a new browser tab open with the contents of the newly saved “plot.html” file.

It is important to note that output_file initiates a persistent mode of operation.

That is, all subsequent calls to show will generate output to the specified file.

We can “reset” where output will go by calling reset_output:

from bokeh.io import reset_output
reset_output()

Git add, commit, push HTML files#

Once you have an html file with an interactive visualization that you are happy with, simply git add, commit, and push the html file to your GitHub repository, which has Github Pages enabled.

Then, you can share the link to the html file on your GitHub repository with others.