[Notebook] Testing Bokeh Interactive Plot on Nikola Sites

This post shows how to add interactive data visualizations to Nikola-powered websites. Everything will be done in Jupyter notebooks! I use Bokeh as an example but similar approaches should work with other JavaScript-based tools like Plotly. For the impatient, drag to the end of this page to see the results.

For deploying website with Nikola, see my previous post.

In [1]:
!nikola --version
Nikola v8.0.1
In [2]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

Some boring stuff to test basic notebook display...

Non-executing code snippet:

print('Hello')

Other languages:

if (i=0; i<n; i++) {
  printf("hello %d\n", i);
}

Latex: $$e^x=\sum_{i=0}^\infty \frac{1}{i!}x^i$$

Table:

This is
a table
In [3]:
# test matplotlib rendering
plt.plot(np.sin(np.linspace(-5, 5)));

Static plots work fine but are a bit boring. Let's create some interactivity.

Interactive visualization with HvPlot and Bokeh

HvPlot provides high-level plotting API built on top of the well-known Bokeh library. Its API is very similar to pandas.DataFrame.plot(), so you don't need to learn too many Bokeh/Holoview-specific syntaxes.

It can be installed easily by pip install hvplot, with all major depedencies.

In [4]:
from IPython.display import HTML

import bokeh
from bokeh.resources import CDN, INLINE
from bokeh.embed import file_html
import holoviews as hv

import pandas as pd
import hvplot.pandas

bokeh.__version__, hv.__version__
Out[4]:
('1.0.4', '1.11.2')

Creating a demo plot

Taken from HvPlot tutorial.

In [5]:
# just some fake data
index = pd.date_range('1/1/2000', periods=1000)
np.random.seed(42)
df = pd.DataFrame(np.random.randn(1000, 4), index=index, columns=list('ABCD')).cumsum()
df.head()
Out[5]:
A B C D
2000-01-01 0.496714 -0.138264 0.647689 1.523030
2000-01-02 0.262561 -0.372401 2.226901 2.290465
2000-01-03 -0.206914 0.170159 1.763484 1.824735
2000-01-04 0.035049 -1.743121 0.038566 1.262447
2000-01-05 -0.977782 -1.428874 -0.869458 -0.149856
In [6]:
plot = df.hvplot()
plot  # The plot shows up in notebook, but not in Nikola-generated website.
Out[6]:

By default the figure is not shown on the Nikola-generated website (i.e. the page you are looking at right now). A few more code is needed for displaying the figure.

Convert to HTML and display

We just need to convert the figure to raw HTML texts and use IPython.display.HTML() to show it:

In [7]:
html_cdn = file_html(hv.render(plot), CDN)  # explained later
HTML(html_cdn)
Out[7]:
Bokeh Application

html_cdn is simply a Python string containing HTML:

In [8]:
type(html_cdn)
Out[8]:
str

It can be generated from a Bokeh figure by bokeh.embed.file_html(). Simplying calling bokeh.embed.file_html(plot) will lead to an error because our plot object is not a bokeh figure yet:

In [9]:
type(plot)
Out[9]:
holoviews.core.overlay.NdOverlay

You need to use hv.render() to convert Holoview output to Bokeh figure.

In [10]:
type(hv.render(plot))
Out[10]:
bokeh.plotting.figure.Figure

So the full command becomes HTML(file_html(hv.render(plot), CDN)). The CDN option says that part of the HTML/CSS/JS code will be retrived from a Content Delivery Network, not stored locally. You may also use INLINE to store all code locally. This allows you to view the HTML file without internet connection but increases the file size:

In [11]:
html_inline = file_html(hv.render(plot), INLINE)
len(html_inline), len(html_cdn)  # The inline one is 5x bigger
Out[11]:
(933634, 203309)

Plot on Maps!

GeoViews is an excellent library for (interactive) geospatial visualizations. It depends on Cartopy, which has better be installed by Conda:

conda install -c conda-forge cartopy  # caution: comes with heavy dependencies!
pip install geoviews
In [12]:
import geoviews as gv
gv.extension('bokeh')
gv.__version__
Out[12]:
'1.6.2'
In [13]:
tile = gv.tile_sources.Wikipedia
tile  # The background map. Can be zoomed-in to a great detail.
Out[13]:
In [14]:
# Again you need a few more code to show the figure on web 
HTML(file_html(hv.render(tile), CDN))
Out[14]:
Bokeh Application
In [15]:
# Overlay some data points
from bokeh.sampledata.airport_routes import airports
airports_filtered = airports[(airports['City'] == 'New York') & (airports['TZ'] == 'America/New_York')]
airports_filtered
Out[15]:
AirportID Name City Country IATA ICAO Latitude Longitude Altitude Timezone DST TZ Type source
282 3697 La Guardia Airport New York United States LGA KLGA 40.777199 -73.872597 21 -5 A America/New_York airport OurAirports
379 3797 John F Kennedy International Airport New York United States JFK KJFK 40.639801 -73.778900 13 -5 A America/New_York airport OurAirports
999 8123 One Police Plaza Heliport New York United States \N NK39 40.712601 -73.999603 244 -5 A America/New_York airport OurAirports
In [16]:
plot_geo = tile * airports_filtered.hvplot.points('Longitude', 'Latitude', geo=True,
                                                  color='red', size=150, width=750, height=500)

HTML(file_html(hv.render(plot_geo), CDN))  # airport locations around NYC
Out[16]:
Bokeh Application

Comments

Comments powered by Disqus