Dashboards (Bokeh)#
Note
This tutorial is adapted from the tutorials on the official Bokeh website.
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.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)
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.