def figure_fn(*kargs, **kwargs): fig = bk_figure(*kargs, **kwargs) fig.title.text_font_size = '14pt' fig.xaxis.axis_label_text_font_size = '24pt' fig.yaxis.axis_label_text_font_size = '24pt' fig.xaxis.major_label_text_font_size = '24pt' fig.yaxis.major_label_text_font_size = '24pt' return fig
def bokeh_plot(histo, jup_url="http://127.0.0.1:8889"): if not isnotebook(): raise NotImplementedError("Only usable in jupyter notebook") import bokeh.plotting.figure as bk_figure from bokeh.io import curdoc, show from bokeh import palettes from bokeh.layouts import row, widgetbox, column from bokeh.models import ColumnDataSource from bokeh.models.widgets import RadioButtonGroup, CheckboxButtonGroup from bokeh.models.widgets import RangeSlider, Div from bokeh.io import output_notebook # enables plot interface in J notebook import numpy as np # init bokeh from bokeh.application import Application from bokeh.application.handlers import FunctionHandler from bokeh.core.validation import silence from bokeh.core.validation.warnings import EMPTY_LAYOUT silence(EMPTY_LAYOUT, True) output_notebook() # Set up widgets cfg_labels = ["Ghost"] wi_config = CheckboxButtonGroup(labels=cfg_labels, active=[0]) wi_dense_select = RadioButtonGroup( labels=[ax.name for ax in histo.dense_axes()], active=0) wi_sparse_select = RadioButtonGroup( labels=[ax.name for ax in histo.sparse_axes()], active=0) # Dense widgets sliders = {} for ax in histo.dense_axes(): edge_vals = (histo.axis(ax.name).edges()[0], histo.axis(ax.name).edges()[-1]) _smallest_bin = np.min(np.diff(histo.axis(ax.name).edges())) sliders[ax.name] = RangeSlider(title=ax.name, value=edge_vals, start=edge_vals[0], end=edge_vals[1], step=_smallest_bin, name=ax.name) # Cat widgets togglers = {} for ax in histo.sparse_axes(): togglers[ax.name] = CheckboxButtonGroup( labels=[i.name for i in ax.identifiers()], active=[0], name=ax.name) # Toggles for all widgets configers = {} for ax in histo.sparse_axes(): configers[ax.name] = CheckboxButtonGroup(labels=["Display", "Ghost"], active=[0, 1], name=ax.name) for ax in histo.dense_axes(): configers[ax.name] = CheckboxButtonGroup(labels=["Display"], active=[0], name=ax.name) # Figure fig = bk_figure(title="1D Projection", plot_width=500, plot_height=500, min_border=20, toolbar_location=None) fig.yaxis.axis_label = "N" fig.xaxis.axis_label = "Quantity" # Iterate over possible overlays _max_idents = 0 # Max number of simultaneou histograms for ax in histo.sparse_axes(): _max_idents = max(_max_idents, len([i.name for i in ax.identifiers()])) # Data source list sources = [] sources_ghost = [] for i in range(_max_idents): sources.append( ColumnDataSource(dict(left=[], top=[], right=[], bottom=[]))) sources_ghost.append( ColumnDataSource(dict(left=[], top=[], right=[], bottom=[]))) # Hist list hists = [] hists_ghost = [] for i in range(_max_idents): if _max_idents < 10: _color = palettes.Category10[min(max(3, _max_idents), 10)][i] else: _color = palettes.magma(_max_idents)[i] hists.append( fig.quad(left="left", right="right", top="top", bottom="bottom", source=sources[i], alpha=0.9, color=_color)) hists_ghost.append( fig.quad(left="left", right="right", top="top", bottom="bottom", source=sources_ghost[i], alpha=0.05, color=_color)) def update_data(attrname, old, new): sparse_active = wi_sparse_select.active sparse_name = [ax.name for ax in histo.sparse_axes()][sparse_active] sparse_other = [ ax.name for ax in histo.sparse_axes() if ax.name != sparse_name ] dense_active = wi_dense_select.active dense_name = [ax.name for ax in histo.dense_axes()][dense_active] dense_other = [ ax.name for ax in histo.dense_axes() if ax.name != dense_name ] # Apply cuts in projections _h = histo.copy() for proj_ax in sparse_other: _idents = histo.axis(proj_ax).identifiers() _labels = [ident.name for ident in _idents] if 0 in configers[proj_ax].active: _h = _h.integrate( proj_ax, [_labels[i] for i in togglers[proj_ax].active]) else: _h = _h.integrate(proj_ax) for proj_ax in dense_other: _h = _h.integrate( proj_ax, slice(sliders[proj_ax].value[0], sliders[proj_ax].value[1])) for cat_ix in range(_max_idents): # Update histo for each toggled overlay if cat_ix in togglers[sparse_name].active: cat_value = histo.axis(sparse_name).identifiers()[cat_ix] h1d = _h.integrate(sparse_name, cat_value) # Get shown histogram values = h1d.project(dense_name).values() if values != {}: h = values[()] bins = h1d.axis(dense_name).edges() # Apply cuts on shown axis bin_los = bins[:-1][ bins[:-1] > sliders[dense_name].value[0]] bin_his = bins[1:][bins[1:] < sliders[dense_name].value[1]] new_bins = np.intersect1d(bin_los, bin_his) bin_ixs = np.searchsorted(bins, new_bins)[:-1] h = h[bin_ixs] sources[cat_ix].data = dict(left=new_bins[:-1], right=new_bins[1:], top=h, bottom=np.zeros_like(h)) else: sources[cat_ix].data = dict(left=[], right=[], top=[], bottom=[]) # Add ghosts if 0 in wi_config.active: h1d = histo.integrate(sparse_name, cat_value) for proj_ax in sparse_other: _idents = histo.axis(proj_ax).identifiers() _labels = [ident.name for ident in _idents] if 1 not in configers[proj_ax].active: h1d = h1d.integrate( proj_ax, [_labels[i] for i in togglers[proj_ax].active]) else: h1d = h1d.integrate(proj_ax) values = h1d.project(dense_name).values() if values != {}: h = h1d.project(dense_name).values()[()] bins = h1d.axis(dense_name).edges() sources_ghost[cat_ix].data = dict( left=bins[:-1], right=bins[1:], top=h, bottom=np.zeros_like(h)) else: sources_ghost[cat_ix].data = dict(left=[], right=[], top=[], bottom=[]) else: sources[cat_ix].data = dict(left=[], right=[], top=[], bottom=[]) sources_ghost[cat_ix].data = dict(left=[], right=[], top=[], bottom=[]) # Cosmetics fig.xaxis.axis_label = dense_name for name, slider in sliders.items(): slider.on_change('value', update_data) for name, toggler in togglers.items(): toggler.on_change('active', update_data) for name, configer in configers.items(): configer.on_change('active', update_data) # Button for w in [wi_dense_select, wi_sparse_select, wi_config]: w.on_change('active', update_data) from bokeh.models.widgets import Panel, Tabs from bokeh.io import output_file, show from bokeh.plotting import figure layout = row( fig, column( Div(text="<b>Overlay Axis:</b>", style={ 'font-size': '100%', 'color': 'black' }), wi_sparse_select, Div(text="<b>Plot Axis:</b>", style={ 'font-size': '100%', 'color': 'black' }), wi_dense_select, Div(text="<b>Categorical Cuts:</b>", style={ 'font-size': '100%', 'color': 'black' }), *[toggler for name, toggler in togglers.items()], Div(text="<b>Dense Cuts:</b>", style={ 'font-size': '100%', 'color': 'black' }), *[slider for name, slider in sliders.items()])) # Config prep incl_lists = [[], [], []] for i, key in enumerate(list(configers.keys())): incl_lists[i // max(5, len(list(configers.keys())) / 3)].append( Div(text="<b>{}:</b>".format(key), style={ 'font-size': '70%', 'color': 'black' })) incl_lists[i // max(5, len(list(configers.keys())) / 3)].append( configers[key]) layout_cfgs = column( row( column( Div(text="<b>Configs:</b>", style={ 'font-size': '100%', 'color': 'black' }), wi_config)), Div(text="<b>Axis togglers:</b>", style={ 'font-size': '100%', 'color': 'black' }), row( column(incl_lists[0]), column(incl_lists[1]), column(incl_lists[2]), )) # Update active buttons def update_layout(attrname, old, new): active_axes = [None] for name, wi in configers.items(): if 0 in wi.active: active_axes.append(name) for child in layout.children[1].children: if child.name not in active_axes: child.visible = False else: child.visible = True for name, configer in configers.items(): configer.on_change('active', update_layout) tab1 = Panel(child=layout, title="Projection") tab2 = Panel(child=layout_cfgs, title="Configs") tabs = Tabs(tabs=[tab1, tab2]) def modify_doc(doc): doc.add_root(row(tabs, width=800)) doc.title = "Sliders" handler = FunctionHandler(modify_doc) app = Application(handler) show(app, notebook_url=jup_url) update_data("", "", "")
def plot(): # Set up initial parameters x = range(11) y = get_infections(10,.2, 1, 2) source = ColumnDataSource(data=dict(x=x, y=y)) # Set up plot plot = bk_figure(plot_height=400, plot_width=600, title="Probability Distribution for the Number of Infected People", tools="crosshair,pan,reset,save,wheel_zoom", x_range=[0, 10], y_range=[0, 1]) plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6) # Set up widgets trans_prob = Slider(title="Person-to-person transmission probability", value=0.2, start=0.0, end=1.0, step=0.01) population = Slider(title="Community population", value=10, start=0, end=140, step=1) prop_infected = Slider(title="Initial proportion of population infected", value=.2, start=0.0, end=1, step=.01) days = Slider(title="Number of days", value=1, start=1, end=14, step=1) mean = Paragraph(text="Expected number of infected people: "+str(round(E(y),2))) # Set up callbacks def update_title(attrname, old, new): plot.title.text = text.value def update_data(attrname, old, new): # Get the current slider values t = trans_prob.value pop = population.value inf = prop_infected.value d = days.value # Generate the new curve x = range(pop+1) y = get_infections(pop, t, d, int(inf*pop)) # update expectation mean.text="Expected number of people infected: "+str(round(E(y),2)) #re-scale so that relevant part of x-axis stays in view plot.x_range.end=0 plot.x_range.end=pop source.data = dict(x=x, y=y) for w in [trans_prob,population,prop_infected,days]: w.on_change('value', update_data) # Set up layouts and add to document inputs = widgetbox(population,trans_prob,prop_infected,days,mean) layout = row(plot, widgetbox(population,trans_prob,prop_infected,days,mean)) def modify_doc(doc): doc.add_root(row(layout, width=800)) doc.title = "Sliders" text.on_change('value', update_title) return layout