Much of the motivation for this section came from the experience of making data apps in R on the Shiny platform.
A simple app for querying the WISE catalog and displaying sources is this one I wrote on a Sunday afternoon for a data science class.
A sophisticated Shiny app is this one for photometric redshift estimation using generalized linear models.
Unfortunately, for publishing data apps, there is not a Python equivalent to Shiny, that makes it easy to host apps and is free of charge. This blog post shows an example from a commercial enterprise.
For now, we will skip the publishing aspect and focus on making a personal app that you run yourself in the notebook (or can give to colleagues to run).
Adding Widgets
Bokeh provides a simple default set of widgets, largely based off the Bootstrap JavaScript library. In the future, it will be possible for users to wrap and use other widget libraries, or their own custom widgets. By themselves, most widgets are not useful. There are two ways to use widgets to drive interactions:
The current value of interactive widgets is available from the .value attribute.
In [ ]:
from bokeh.io import vform
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import Figure, output_file, show
output_file("callback.html")
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
plot = Figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
callback = CustomJS(args=dict(source=source), code="""
var data = source.get('data');
var f = cb_obj.get('value')
x = data['x']
y = data['y']
for (i = 0; i < x.length; i++) {
y[i] = Math.pow(x[i], f)
}
source.trigger('change');
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=callback)
layout = vform(slider, plot)
show(layout)
The next cell shows the start of how to set up something like the WISE app.
In [ ]:
from bokeh.models.widgets import Slider, RadioGroup, Button
from bokeh.io import output_file, show, vform
from bokeh.plotting import figure
output_file("queryWise.html")
band = RadioGroup(labels=["3.5 microns", "4.5 microns",
"12 microns", "22 microns"], active=0)
fov = Slider(start=5, end=15, value=5, step=.25, title="Field of View (arcmin)")
ra = Slider(start=0, end=359, value=120, step=1, title="Right Ascension (degrees)")
dec = Slider(start=-90, end=90, value=0, step=1, title="Declination (degrees)")
button = Button(label="Submit")
p = figure(plot_width=400, plot_height=400,
tools="tap", title="WISE sources")
p.circle(ra.value, dec.value)
show(vform(fov,band,ra,dec,button, p))
Since the slider does not display...there is some problem with my installation for using widgets in the notebook.
In [ ]:
from ipywidgets import *
In [ ]:
from IPython.display import display
fov = FloatSlider(value = 5.0,
min = 5.0,
max = 15.0,
step = 0.25)
display(fov)
In [ ]:
%matplotlib notebook
import pandas as pd
import matplotlib.pyplot as plt
from ipywidgets import *
from IPython.display import display
##from jnotebook import display
from IPython.html import widgets
plt.style.use('ggplot')
NUMBER_OF_PINGS = 4
#displaying the text widget
text = widgets.Text(description="Domain to ping", width=200)
display(text)
#preparing the plot
data = pd.DataFrame()
x = range(1,NUMBER_OF_PINGS+1)
plots = dict()
fig, ax = plt.subplots()
plt.xlabel('iterations')
plt.ylabel('ms')
plt.xticks(x)
plt.show()
#preparing a container to put in created checkbox per domain
checkboxes = []
cb_container = widgets.HBox()
display(cb_container)
#add button that updates the graph based on the checkboxes
button = widgets.Button(description="Update the graph")
#function to deal with the added domain name
def handle_submit(sender):
#a part of the magic inside python : pinging
res = !ping -c {NUMBER_OF_PINGS} {text.value}
hits = res.grep('64 bytes').fields(-2).s.replace("time=","").split()
if len(hits) == 0:
print("Domain gave error on pinging")
else:
#rebuild plot based on ping result
data[text.value] = hits
data[text.value] = data[text.value].astype(float)
plots[text.value], = ax.plot(x, data[text.value], label=text.value)
plt.legend()
plt.draw()
#add a new checkbox for the new domain
checkboxes.append(widgets.Checkbox(description = text.value, value=True, width=90))
cb_container.children=[i for i in checkboxes]
if len(checkboxes) == 1:
display(button)
#function to deal with the checkbox update button
def on_button_clicked(b):
for c in cb_container.children:
if not c.value:
plots[c.description].set_visible(False)
else:
plots[c.description].set_visible(True)
plt.legend()
plt.draw()
button.on_click(on_button_clicked)
text.on_submit(handle_submit)
plt.show()
In [ ]: