So I've got a variety of pandas dataframes and I'd like to reuse a bokeh app to view them, rather than hard coding the data into the app. I've adapted the crossfilter example to show the problem with my approach.
This should be called as
dframe = some Pandas dataframe
invokeh(bk_crossfilter, dframe)
and defined as in the code block below.
It seems that bokeh doesn't like the use of functools.partial in the definition of the app below and gives me a 500 server error in the browser. Earlier today the print statement in the bokeh app was showing the dataframe, but that now seems to get mangled into bokeh.Document during the function entry. The console output is now:
Preparing a bokeh application.
Opening Bokeh application on http://localhost:5006/
CROSSFILTER: <bokeh.document.document.Document object at 0x0000029BAB2783C8>
ERROR:tornado.application:Uncaught exception GET / (::1)
HTTPServerRequest(protocol='http', host='localhost:5006', method='GET', uri='/', version='HTTP/1.1', remote_ip='::1')
Traceback (most recent call last):
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\site-packages\tornado\web.py", line 1703, in _execute
result = await result
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\site-packages\bokeh\server\views\doc_handler.py", line 52, in get
session = await self.get_session()
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\site-packages\bokeh\server\views\session_handler.py", line 120, in get_session
session = await self.application_context.create_session_if_needed(session_id, self.request, token)
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\site-packages\bokeh\server\contexts.py", line 218, in create_session_if_needed
self._application.initialize_document(doc)
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\site-packages\bokeh\application\application.py", line 171, in initialize_document
h.modify_document(doc)
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\site-packages\bokeh\application\handlers\function.py", line 132, in modify_document
self._func(doc)
File "c:\users\jdorsey\code\utils\dorsey\visualisation.py", line 36, in bk_crossfilter
columns = sorted(df.columns)
AttributeError: 'Document' object has no attribute 'columns'
ERROR:tornado.access:500 GET / (::1) 7.98ms
WARNING:tornado.access:404 GET /favicon.ico (::1) 0.99ms
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "c:\users\jdorsey\code\utils\dorsey\visualisation.py", line 27, in invokeh
io_loop.start()
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\site-packages\tornado\platform\asyncio.py", line 149, in start
self.asyncio_loop.run_forever()
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\asyncio\base_events.py", line 422, in run_forever
self._run_once()
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\asyncio\base_events.py", line 1396, in _run_once
event_list = self._selector.select(timeout)
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\selectors.py", line 323, in select
r, w, _ = self._select(self._readers, self._writers, [], timeout)
File "C:\Users\jdorsey\code\conda\environment\REDACTED\lib\selectors.py", line 314, in _select
r, w, x = select.select(r, w, w, timeout)
Adding a print statement to the invokeh function confirms dataset is a valid pandas frame there.
[1379 rows x 82 columns]
And the code:
from tornado.ioloop import IOLoop
from bokeh.application.handlers import FunctionHandler
from bokeh.application import Application
from bokeh.server.server import Server
from bokeh.layouts import column, row
from bokeh.models import Select
from bokeh.palettes import Spectral5
from bokeh.plotting import curdoc, figure
from functools import partial
def invokeh(visual,dataset):
"""
Launch a bokeh server and connect to it.
ARGUMENTS:
visual: A bokeh visualisation
dataset: A dataset to populate the visualisation
"""
print("Preparing a bokeh application.")
io_loop = IOLoop.current()
bokeh_app = Application(FunctionHandler(partial(visual,dataset)))
server = Server({"/": bokeh_app})#, io_loop=io_loop)
server.start()
print("Opening Bokeh application on http://localhost:5006/")
io_loop.add_callback(server.show, "/")
io_loop.start()
def bk_crossfilter(doc,df):
print('CROSSFILTER: ', df)
SIZES = list(range(6, 22, 3))
COLORS = Spectral5
N_SIZES = len(SIZES)
N_COLORS = len(COLORS)
columns = sorted(df.columns)
discrete = [x for x in columns if df[x].dtype == object]
continuous = [x for x in columns if x not in discrete]
def create_figure():
xs = df[x.value].values
ys = df[y.value].values
x_title = x.value.title()
y_title = y.value.title()
kw = dict()
if x.value in discrete:
kw['x_range'] = sorted(set(xs))
if y.value in discrete:
kw['y_range'] = sorted(set(ys))
kw['title'] = "%s vs %s" % (x_title, y_title)
p = figure(plot_height=600, plot_width=800, tools='pan,box_zoom,hover,reset', **kw)
p.xaxis.axis_label = x_title
p.yaxis.axis_label = y_title
if x.value in discrete:
p.xaxis.major_label_orientation = pd.np.pi / 4
sz = 9
if size.value != 'None':
if len(set(df[size.value])) > N_SIZES:
groups = pd.qcut(df[size.value].values, N_SIZES, duplicates='drop')
else:
groups = pd.Categorical(df[size.value])
sz = [SIZES[xx] for xx in groups.codes]
c = "#31AADE"
if color.value != 'None':
if len(set(df[color.value])) > N_COLORS:
groups = pd.qcut(df[color.value].values, N_COLORS, duplicates='drop')
else:
groups = pd.Categorical(df[color.value])
c = [COLORS[xx] for xx in groups.codes]
p.circle(x=xs, y=ys, color=c, size=sz, line_color="white", alpha=0.6, hover_color='white', hover_alpha=0.5)
return p
def update(attr, old, new):
layout.children[1] = create_figure()
print("COLUMNS: ", columns[0], columns[1])
print("DISCRETE: ", discrete, " CONTINUOUS: ", continuous)
x = Select(title='X-Axis', value=columns[0], options=columns)
x.on_change('value', update)
y = Select(title='Y-Axis', value=columns[1], options=columns)
y.on_change('value', update)
size = Select(title='Size', value='None', options=['None'] + continuous)
size.on_change('value', update)
color = Select(title='Color', value='None', options=['None'] + continuous)
color.on_change('value', update)
controls = column(x, y, color, size, width=200)
layout = row(controls, create_figure())
curdoc().add_root(layout)
curdoc().title = "Crossfilter"
Thanks in advance for any suggestions.