bokeh 5th: project interactive data visualization web app

 

How to make useful and fun interactive data visualization web apps and how to deploy them online for public access? 

Bokeh Plot

Click App runs on Here, will be interactive and animated.


Click App runs on Here, will be interactive and animated.

Bokeh Plot
bokeh5

gapminder project

In [1]:
from bokeh.sampledata.gapminder import life_expectancy, population, fertility, regions

import pandas as pd
from bokeh.io import output_file, output_notebook, show, curdoc
from bokeh.plotting import figure
from bokeh.models import HoverTool, ColumnDataSource

from bokeh.charts import Scatter
In [2]:
output_notebook()
Loading BokehJS ...
In [3]:
life_expectancy.shape, population.shape, fertility.shape, regions.shape
Out[3]:
((244, 50), (244, 50), (244, 50), (244, 2))
In [4]:
life_expectancy.head(1)
Out[4]:
1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 ... 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
Country
Afghanistan 33.639 34.152 34.662 35.17 35.674 36.172 36.663 37.143 37.614 38.075 ... 56.583 57.071 57.582 58.102 58.618 59.124 59.612 60.079 60.524 60.947

1 rows × 50 columns

In [5]:
population.head(1)
Out[5]:
1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 ... 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
Country
Afghanistan 10474903.0 10697983.0 10927724.0 11163656.0 11411022.0 11676990.0 11964906.0 12273101.0 12593688.0 12915499.0 ... 26693486.0 27614718.0 28420974.0 29145841.0 29839994.0 30577756.0 31411743.0 32358260.0 33397058.0 34499915.0

1 rows × 50 columns

In [6]:
fertility.head(1)
Out[6]:
1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 ... 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
Country
Afghanistan 7.671 7.671 7.671 7.671 7.671 7.671 7.671 7.671 7.671 7.671 ... 7.136 6.93 6.702 6.456 6.196 5.928 5.659 5.395 5.141 4.9

1 rows × 50 columns

In [7]:
regions.head(1)
Out[7]:
Group ID
Country
Angola Sub-Saharan Africa AO
In [8]:
life2000=life_expectancy['2000'].to_frame()
fert2000=fertility['2000'].to_frame()
pop2000=population['2000'].to_frame()
In [9]:
two=pd.merge(life2000,fert2000,left_index=True,right_index=True)
In [10]:
three=pd.merge(two, pop2000,left_index=True,right_index=True )
In [11]:
# pd.concat([life2000,fert2000],axis=1)

three['country']=three.index
In [12]:
three.columns=['life2000','fertility2000','population2000','country']
In [13]:
three.head(1)
Out[13]:
life2000 fertility2000 population2000 country
Country
Afghanistan 54.863 7.733 22856302.0 Afghanistan
In [14]:
p = figure(title='2000', x_axis_label='Fertility (children per woman)', y_axis_label='Life Expectancy (years)',
           plot_height=400, plot_width=700,
#            tools=[HoverTool(tooltips=[('where','@country')])],
           tools=[HoverTool(tooltips='@country')],
          )

data_source = ColumnDataSource(data={
    'x'       : three.fertility2000,
    'y'       : three.life2000,
    'country' :three['country'],
})

p.circle(x='x', y='y', source=data_source, size=12, alpha=.4, color='red')


curdoc().add_root(p)
curdoc().title = 'Gapminder'
show(p)
  • add hover tool to Scatter charts
  • over tool can be added to line charts as well
In [15]:
# p=Scatter(two, x='life2000',y='fertility2000',
#          title='life vs fertility in 2000',
#          plot_width=400,plot_height=200,
#          tooltips=[('where','@country')])

# show(p)

interactive deploy on server

  • add Slider
  • deploy on server
In [16]:
from bokeh.sampledata.gapminder import life_expectancy, population, fertility, regions

from bokeh.io import output_file, output_notebook, show, curdoc
from bokeh.plotting import figure
from bokeh.models import HoverTool, ColumnDataSource
In [17]:
from bokeh.layouts import widgetbox, row, column
from bokeh.models import Slider

from bokeh.models import LinearColorMapper
In [18]:
data_column=ColumnDataSource(data={
            'fertility'       : fertility['1970'],
            'life'       : life_expectancy['1970'],
            'country' : population.index,
            'pop'     : (population['1970']/ 20000000) + 2,
        })
In [19]:
def update_plot(attr, old, new):

    yr = slider.value
    yr=str(yr)
    
    new_data = {
            'fertility'       : fertility[yr],
            'life'       : life_expectancy[yr],
            'country' : population.index,
            'pop'     : (population[yr]/ 20000000) + 2,
        }
    data_column.data = new_data
    p.title.text = "Gapminder data for %s"% yr
In [20]:
slider = Slider(start=1964,end=2013,step=1,value=1970,title='Year')
slider.on_change('value',update_plot)

try add diff colors

In [21]:
mapper=LinearColorMapper(['silver','orange','purple','red'])
In [ ]:
 
In [22]:
TOOLS="crosshair,pan,wheel_zoom,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"


p = figure(title='Gapminder', x_axis_label='Fertility (children per woman)', y_axis_label='Life Expectancy (years)',
           plot_height=600, plot_width=950,
#            tools=[HoverTool(tooltips=[('where','@country')])],
           tools=[HoverTool(tooltips='@country',mode='vline'),TOOLS])

p.circle(x='fertility', y='life', source=data_column, size=15,
         alpha=.4, hover_color='green',
         color={'field':'fertility','transform':mapper})
Out[22]:
<bokeh.models.renderers.GlyphRenderer at 0x7f5070efae90>
In [23]:
layout = column(widgetbox(slider), p)
curdoc().add_root(layout)
curdoc().title = 'Gapminder_server'

show(layout)

add dropdowns

Project

deploy on AWS

  • using Flask
  • via Nginx
In [24]:
from bokeh.sampledata.gapminder import life_expectancy, population, fertility, regions

from bokeh.io import output_file, output_notebook, show, curdoc
from bokeh.plotting import figure
from bokeh.models import HoverTool, ColumnDataSource

from bokeh.layouts import widgetbox, row, column
from bokeh.models import Slider

from bokeh.models import LinearColorMapper

from bokeh.models import Select, Button, Label

from bokeh.models.widgets import Div

# population=population/100000000
from numpy import log10
population=log10(population)



create={'fertility':fertility, 'life':life_expectancy, 'population':population}

mapper=LinearColorMapper(['silver','orange','purple','red'])
TOOLS="crosshair,pan,wheel_zoom,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"


data_column=ColumnDataSource(data={
            'x'       : create['fertility']['1970'],
            'y'       : create['life']['1970'],
            'country' : population.index,
            'pop':population['1970']*3
        })
In [25]:
def update_plot(attr, old, new):

    yr = slider.value
    yr=str(yr)
    
    x = x_select.value
    y = y_select.value    
    
    
    
    new_data = {
            'x'       : create[x][yr],
            'y'       : create[y][yr],
            'country' : population.index,
            'pop':population[yr]*3
        }
    data_column.data = new_data
    p.title.text = "Gapminder data for %s"% yr
    p.xaxis.axis_label=x
    p.yaxis.axis_label=y
    

# Set the range of all axes
    p.x_range.start = min(create[x][yr])
    p.x_range.end = max(create[x][yr])
    p.y_range.start = min(create[y][yr])
    p.y_range.end = max(create[y][yr])    
    

    


p = figure(title='Gapminder', x_axis_label='Fertility (children per woman)', y_axis_label='Life Expectancy (years)',
           plot_height=600, plot_width=950,
#            tools=[HoverTool(tooltips=[('where','@country')])],
           tools=[HoverTool(tooltips='@country',mode='vline'),TOOLS])

p.circle(x='x', y='y', source=data_column, size='pop',
         alpha=.4, hover_color='green',
         color={'field':'x','transform':mapper})
Out[25]:
<bokeh.models.renderers.GlyphRenderer at 0x7f5070e8ff10>
In [26]:
div = Div(text="""<br><hr><span style="font-size: 12pt;">This is a Data Visualization web app test I created using <span style="background-color: #deffde;">Bokeh</span> and deployed in my <span style="background-color: #deffde;">AWS</span>.</span>

<br>
<p style="text-align: right;"><span style="font-size: 16pt;">Go <span style="background-color: #deeeff;">back</span> to <span style="color: #ae5eff;"><a style="color: #ae5eff;" href="http://shichaoji.com">my blog</a></span></span>
<span style="font-size: 12pt;"><br><br><em><span style="color: #007fff;">Shichao Ji</span></em></span></p>""",
width=200, height=100)
In [27]:
x_select = Select(
    options=['fertility', 'life', 'population'],
    value='fertility',
    title='x-axis data'
)

y_select = Select(
    options=['fertility', 'life', 'population'],
    value='life',
    title='y-axis data'
)

# Attach the update_plot callback to the 'value' property of x_select
x_select.on_change('value', update_plot)
y_select.on_change('value', update_plot)


slider = Slider(start=1964,end=2013,step=1,value=1970,title='Year')
slider.on_change('value',update_plot)




def animate_update():
    year = slider.value + 1
    if year > 2013:
        year = 1964
    slider.value = year




def animate():
    if button.label == 'Play':
        button.label = 'Pause'
        curdoc().add_periodic_callback(animate_update, 200)
    else:
        button.label = 'Play'
        curdoc().remove_periodic_callback(animate_update)

button = Button(label='Play', width=120)
button.on_click(animate)




layout = row(widgetbox(slider, x_select, y_select,button, div), p)
curdoc().add_root(layout)
curdoc().title = 'Gapminder_server'

show(layout)
In [28]:
output_notebook()
Loading BokehJS ...
In [29]:
curdoc().add_periodic_callback
Out[29]:
<bound method Document.add_periodic_callback of <bokeh.document.Document object at 0x7f507105d850>>
In [ ]:
 

the source code that I deployed on AWS

In [30]:
from bokeh.sampledata.gapminder import life_expectancy, population, fertility, regions

from bokeh.io import output_file, output_notebook, show, curdoc
from bokeh.plotting import figure
from bokeh.models import HoverTool, ColumnDataSource

from bokeh.layouts import widgetbox, row, column
from bokeh.models import Slider

from bokeh.models import LinearColorMapper

from bokeh.models import Select, Button, Label
from bokeh.models.widgets import Div

##population=population/20000000 +2
from numpy import log10, exp

pop=log10(population)
population=population/1000000


create={'fertility':fertility, 'life':life_expectancy, 'log10(population)':pop}


mapper=LinearColorMapper(['green','orange','purple','red'])

TOOLS="crosshair,pan,wheel_zoom,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"


data_column=ColumnDataSource(data={
            'x'       : create['fertility']['1970'],
            'y'       : create['life']['1970'],
            'country' : population.index,
            'pop': exp(pop['1970'])/100*1.3,
            'population':population['1970'],
        })




def update_plot(attr, old, new):

    yr = slider.value
    yr=str(yr)
    
    x = x_select.value
    y = y_select.value    
    
    
    
    new_data = {
            'x'       : create[x][yr],
            'y'       : create[y][yr],
            'country' : population.index,
            'pop': exp(pop[yr])/100*1.3,
            'population':population[yr],
        }
    data_column.data = new_data
    p.title.text = "Gapminder data for %s"% yr
    p.xaxis.axis_label=x
    p.yaxis.axis_label=y
    
# Set the range of all axes
    p.x_range.start = min(create[x][yr])
    p.x_range.end = max(create[x][yr])
    p.y_range.start = min(create[y][yr])
    p.y_range.end = max(create[y][yr])


    
tips=[('Country','@country'),('X value','@x'),('Y value','@y'),('Population','@population million')]

p = figure(title='Gapminder', x_axis_label='Fertility (children per woman)', y_axis_label='Life Expectancy (years)',
           plot_height=600, plot_width=950,
           
           tools=[HoverTool(tooltips=tips),TOOLS],
           )

p.circle(x='x', y='y', source=data_column, size='pop',
         alpha=.4, hover_color='cyan',
         color={'field':'x','transform':mapper})



div = Div(text="""<br><br><hr><span style="font-size: 12pt;">This is a Data Visualization web app test I created using <span style="background-color: #deffde;">Bokeh</span> and deployed in my <span style="background-color: #deffde;">AWS</span>.</span>

<br>
<p style="text-align: right;"><span style="font-size: 16pt;">Go <span style="background-color: #deeeff;">back</span> to <span style="color: #ae5eff;"><a style="color: #ae5eff;" href="http://shichaoji.com">my blog</a></span></span>
<span style="font-size: 12pt;"><br><br><em><span style="color: #007fff;">Shichao Ji</span></em></span></p>""",
width=200, height=100)



x_select = Select(
    options=['fertility', 'life', 'log10(population)'],
    value='fertility',
    title='x-axis data'
)

y_select = Select(
    options=['fertility', 'life', 'log10(population)'],
    value='life',
    title='y-axis data'
)

# Attach the update_plot callback to the 'value' property of x_select
x_select.on_change('value', update_plot)
y_select.on_change('value', update_plot)


slider = Slider(start=1964,end=2013,step=1,value=1970,title='Year')
slider.on_change('value',update_plot)

def animate_update():
    year = slider.value + 1
    if year > 2013:
        year = 1964
    slider.value = year




def animate():
    if button.label == 'Play':
        button.label = 'Pause'
        curdoc().add_periodic_callback(animate_update, 200)
    else:
        button.label = 'Play'
        curdoc().remove_periodic_callback(animate_update)

button = Button(label='Play', width=120)
button.on_click(animate)



layout = row(widgetbox(slider, x_select, y_select,  button, div), p)

# curdoc().add_root(layout)

curdoc().title = 'Gapminder_server'

show(layout)
In [ ]:
 

Leave a Reply

Your email address will not be published.