Search code examples
pythonhtmljupyterbokeh

Jupyter interaction is not exported into the html


I've recently got into coding and am now running a data analysis on open source Corona data. I built an interactive graph using Python3 inside of Jupyter Notebook. The only thing is, I built in an interaction but it is only showing inside of the Notebook, not when it is exported into a html. Could someone give me a hint of why this is? Many thanks in advance.

Code:

# Import the necessary packages

import pandas as pd
import numpy as np 
import requests
import io

from bokeh.io import push_notebook,output_file
from bokeh.io import show, curdoc
from bokeh.plotting import figure, output_notebook
from bokeh.models import HoverTool, ColumnDataSource, Select
from bokeh.layouts import row
from bokeh.models.tickers import FixedTicker
from bokeh.models.callbacks import CustomJS

from ipywidgets import interact

output_notebook()

#---------------------------------------------------------------------------------------------------   
# Import the data
#---------------------------------------------------------------------------------------------------

url = 'https://data.humdata.org/hxlproxy/api/data-preview.csv?url=https%3A%2F%2Fraw.githubusercontent.com%2FCSSEGISandData%2FCOVID-19%2Fmaster%2Fcsse_covid_19_data%2Fcsse_covid_19_time_series%2Ftime_series_covid19_confirmed_global.csv&filename=time_series_covid19_confirmed_global.csv'
s=requests.get(url).content

url2 = 'https://data.humdata.org/hxlproxy/api/data-preview.csv?url=https%3A%2F%2Fraw.githubusercontent.com%2FCSSEGISandData%2FCOVID-19%2Fmaster%2Fcsse_covid_19_data%2Fcsse_covid_19_time_series%2Ftime_series_covid19_deaths_global.csv&filename=time_series_covid19_deaths_global.csv'
s2 =requests.get(url2).content

df = pd.read_csv(io.StringIO(s.decode('utf-8')))
df = df.fillna("")

df2 = pd.read_csv(io.StringIO(s2.decode('utf-8')))
df2 = df2.fillna("")

#--------------------------------------------------------------------------------------------------- 
# Number of reported Corona cases
#---------------------------------------------------------------------------------------------------

Numb_cases = df.iloc[:,0:2].merge(df.iloc[:,4:],how='inner',right_index=True,left_index=True).fillna("")
Numb_cases.iloc[:,1] = Numb_cases.iloc[:,1]+ " : " + Numb_cases.iloc[:,0]
Numb_cases = Numb_cases.iloc[:,1:]
Numb_cases = Numb_cases.transpose()
Numb_cases.columns = Numb_cases.iloc[0,:]
Numb_cases = Numb_cases.iloc[1:,:]
Numb_cases.index = pd.to_datetime(Numb_cases.index)


#---------------------------------------------------------------------------------------------------
# Number of reported Corona deaths
#---------------------------------------------------------------------------------------------------

Numb_deaths = df2.iloc[:,0:2].merge(df2.iloc[:,4:],how='inner',right_index=True,left_index=True).fillna("")
Numb_deaths.iloc[:,1] = Numb_deaths.iloc[:,1]+ " : " + Numb_deaths.iloc[:,0]
Numb_deaths = Numb_deaths.iloc[:,1:]
Numb_deaths = Numb_deaths.transpose()
Numb_deaths.columns = Numb_deaths.iloc[0,:]
Numb_deaths = Numb_deaths.iloc[1:,:]

dates = pd.DataFrame(Numb_deaths.index)

Numb_deaths.index = pd.to_datetime(Numb_deaths.index)

#---------------------------------------------------------------------------------------------------
# Create the interactive graphs
#---------------------------------------------------------------------------------------------------

x = Numb_deaths.index
countries = list(Numb_cases.columns)

#plot1
source = ColumnDataSource(data={
'x' : Numb_cases.index,
'y' : Numb_cases
})

p = figure(x_axis_type="datetime", plot_width=400, plot_height=400, tools="box_zoom,reset")

p.yaxis.axis_label = "Total Number of Cases"
p.xaxis.axis_label = "Date"

countries = list(Numb_cases.columns)

# Create a HoverTool: hover
hover = HoverTool(tooltips = [('Numb cases:', '@y'),('Date :','$x{%F}')],formatters={'$x': 'datetime'},mode='mouse')

# Add the hover tool to the figure p
p.add_tools(hover)


#Plot2
source2 = ColumnDataSource(data={
'x' : Numb_deaths.index,
'y' : Numb_deaths
})

p2 = figure(x_axis_type="datetime", plot_width=400, plot_height=400,tools="box_zoom,reset")

p2.yaxis.axis_label = "Total Number of Deaths"
p2.xaxis.axis_label = "Date"


countries = list(Numb_deaths.columns)

# Create a HoverTool: hover
hover = HoverTool(tooltips = [('Numb deaths:', '@y'),('Date :','$x{%F}')],formatters={'$x': 'datetime'},mode='mouse')

# Add the hover tool to the figure p
p2.add_tools(hover)

# Define a callback function: update_plot
def update(Country):
source.data = {
        'x' : x,
        'y' : Numb_cases.loc[:,Country]
    }
p.circle('x', 'y',source=source ,size=10,
     fill_color='grey', alpha=0.40, line_color='grey',
     hover_fill_color='firebrick', hover_alpha=0.90,
     hover_line_color='white')
source2.data = {
        'x' : x,
        'y' : Numb_deaths.loc[:,Country]
    }
p2.circle('x', 'y',source=source2 ,size=10,
     fill_color='grey', alpha=0.40, line_color=None,
     hover_fill_color='firebrick', hover_alpha=0.90, 
     hover_line_color='white')
push_notebook()

interact(update, Country=countries)

show(row(p,p2),notebook_handle=True)

Graph inside of Jupyter Notebook

Graph inside of an exported html


Solution

  • When you're working with a notebook, you have a server running somewhere that runs that Python code inside the update function. But when you have just a static HTML page, you don't have anything that runs Python - that's why the function is not working.

    You either need to keep using the notebook or use bokeh serve. It's also possible to embed Bokeh into some existing server, like Flask or Django. The last option is to rewrite the update function in JavaScript so it can be embedded in HTML.