def test_callback_property_executes(self, bokeh_model_page): group = CheckboxGroup(labels=LABELS, css_classes=["foo"]) group.callback = CustomJS(code=RECORD("active", "cb_obj.active")) page = bokeh_model_page(group) el = page.driver.find_element_by_css_selector( 'div.foo div label input[value="2"]') el.click() results = page.results assert results['active'] == [2] el = page.driver.find_element_by_css_selector( 'div.foo div label input[value="0"]') el.click() results = page.results assert results['active'] == [0, 2] el = page.driver.find_element_by_css_selector( 'div.foo div label input[value="2"]') el.click() results = page.results assert results['active'] == [0] assert page.has_no_console_errors()
def test_callback_property_executes(self, bokeh_model_page): group = CheckboxGroup(labels=LABELS, css_classes=["foo"]) group.callback = CustomJS(code=RECORD("active", "cb_obj.active")) page = bokeh_model_page(group) el = page.driver.find_element_by_css_selector('.foo input[value="2"]') el.click() results = page.results assert results['active'] == [2] el = page.driver.find_element_by_css_selector('.foo input[value="0"]') el.click() results = page.results assert results['active'] == [0, 2] el = page.driver.find_element_by_css_selector('.foo input[value="2"]') el.click() results = page.results assert results['active'] == [0] assert page.has_no_console_errors()
def plotting(feat1, feat2, playlists): '''Plot the two features feat1 & feat2 for all playlists in dictionary "playlists" Arguments -------- feat1 = str, feature 1 feat2 = str, feature 2 playlists = dictionary of playlist names and song feature object returned by spotifyuser.get_features_playlist() Returns ------- Bokeh plot with hover, reset and checkbox ability. ''' hover = HoverTool(tooltips=[("Name", "@name"), ("Artist", "@artist")]) q = figure(plot_width=600, plot_height=600, tools=[hover, ResetTool()], title="Feature Plot", x_axis_label=feat1, y_axis_label=feat2) dots = collections.OrderedDict() labels = [] active = [] counter = 0 for key in playlists: dots["scat_%02d"%counter] = q.circle(feat1, feat2, size=5, source=ColumnDataSource(playlists[key]), \ color= cscheme[counter %8], legend = key, alpha = 1.0) labels.append(key) active.append(counter) counter += 1 #Create code for checkbox code_block = "%s.visible = %s in checkbox.active;\ " code = "" counter = 0 for key in dots: code = code + (code_block % (key, str(counter))) counter += 1 code = code + "show(checkbox.active)" #Create checkbox object checkbox = CheckboxGroup(labels=labels, active=active, width=100) args = dots args["checkbox"] = checkbox #checkbox.callback = CustomJS(args=args, lang="coffeescript", code=code) checkbox.callback = CustomJS(args=args, code=code) layout = row(q, checkbox) show(layout)
""" import numpy as np from bokeh.io import output_file, show from bokeh.layouts import row from bokeh.palettes import Viridis3 from bokeh.plotting import figure from bokeh.models import CheckboxGroup, CustomJS output_file("line_on_off.html", title="line_on_off.py example") p = figure() props = dict(line_width=4, line_alpha=0.7) x = np.linspace(0, 4 * np.pi, 100) l0 = p.line(x, np.sin(x), color=Viridis3[0], legend_label="Line 0", **props) l1 = p.line(x, 4 * np.cos(x), color=Viridis3[1], legend_label="Line 1", **props) l2 = p.line(x, np.tan(x), color=Viridis3[2], legend_label="Line 2", **props) checkbox = CheckboxGroup(labels=["Line 0", "Line 1", "Line 2"], active=[0, 1, 2], width=100) checkbox.callback = CustomJS(args=dict(l0=l0, l1=l1, l2=l2, checkbox=checkbox), code=""" l0.visible = 0 in checkbox.active; l1.visible = 1 in checkbox.active; l2.visible = 2 in checkbox.active; """) layout = row(checkbox, p) show(layout)
def render_mask(img_path, document_objs, document_name='Default_Document', output_path='./outputs/', save_html=False): img = load_img(img_path) bokeh.plotting.reset_output() hover = HoverTool(tooltips=[ ("Name", "@name"), ("(x,y)", "($x, $y)"), ]) fig = figure( tools=[hover, bokeh.models.WheelZoomTool(), bokeh.models.PanTool()]) fig.hover.point_policy = "follow_mouse" fig.sizing_mode = 'scale_width' fig.image_url(url=[os.path.basename(img_path)], x=0, y=img.shape[0], w=img.shape[1], h=img.shape[0]) script = "active = cb_obj.active;" labels = list(document_objs.keys()) color_map = bp.magma(len(labels)) args = dict() total_objs = 0 for key_id, key in enumerate(labels): xs = [] ys = [] for box in document_objs[key]: _ = np.array(box).reshape(-1, 2).T xs.append(_[0]) ys.append(_[1]) ys = doccoor2planecoor(ys, img.shape[0]) data_source = dict( x=xs, y=ys, name=["%s %d" % (key, idx) for idx in range(len(xs))]) falpha = 0.5 * int('table' not in key.lower()) lcolor = 'blue' if 'table' in key.lower() else 'red' lwidth = 3 if 'table' in key.lower() else 1 total_objs += len(document_objs[key]) args[key] = fig.patches('x', 'y', source=data_source, fill_color=color_map[key_id], fill_alpha=falpha, line_color=lcolor, line_width=lwidth, legend=key + ': %d' % len(document_objs[key])) r = args[key] script += "\n%s.visible = active.includes(%d);" % (key, key_id) fig.patch([], [], fill_color='red', fill_alpha=0, line_color='white', legend='Total region: %d' % total_objs) fig.legend.click_policy = "hide" fig.legend.location = "top_left" checkbox = CheckboxGroup(labels=labels, active=[]) #active=[*range(len(labels))]) checkbox.callback = CustomJS(args=args, code=script) plt_obj = [fig] html = file_html(plt_obj, CDN, title=document_name) if save_html: base = os.path.join(output_path, document_name) if os.path.exists(base): shutil.rmtree(base) os.makedirs(base) shutil.copy(img_path, base) with open(os.path.join(base, 'main.html'), 'w') as g: g.write(html) return html
active=range(len(lat_ordered_sites)), width=200) #### checkbox_iterable = [('p' + str(i), plots[i]) for i in N_plots] + [ ('pcor' + str(i), corplots[i]) for i in N_corplots ] + [('checkbox', checkbox)] checkbox_code = ''.join([ 'p' + str(i) + '.visible = checkbox.active.includes(' + str(i / 2) + ');p' + str(i + 1) + '.visible = checkbox.active.includes(' + str(i / 2) + ');pcor' + str(i / 2) + '.visible = checkbox.active.includes(' + str(i / 2) + ');' for i in range(0, len(N_plots), 2) ]) checkbox.callback = CustomJS( args={key: value for key, value in checkbox_iterable}, code=checkbox_code) # button to uncheck all checkboxes clear_button = Button(label='Clear all', width=120) clear_button_code = """checkbox.active=[];""" + checkbox_code clear_button.callback = CustomJS( args={key: value for key, value in checkbox_iterable}, code=clear_button_code) # button to check all checkboxes check_button = Button(label='Check all', width=120) check_button_code = """checkbox.active=""" + str( N_plots) + """;""" + checkbox_code check_button.callback = CustomJS(
def create_plot(units, df_list): tools = "pan,box_zoom,reset" p = figure(x_axis_type='datetime', tools=tools) p.toolbar.logo = None p.yaxis[0].ticker.desired_num_ticks = 4 p.yaxis.axis_label = units props = dict(line_width=4, line_alpha=0.7, hover_line_alpha=1.0) colors = Category10[10] glyph_dict = {} labels = [] active = [] items = [] sources = [] names = 'abcdefghijklmnopqrstuvwxyz' callback_string = '{}.visible = {} in checkbox.active;' code_string = '' i = 0 for df in df_list: legend = df.columns[0] series = df.iloc[:, 0] labels.append(legend) x = series.index y = series.values.round(2) source = ColumnDataSource(data={ 'x': x, 'y': y, 'date': [str(x) for x in x] }) sources.append(source) line = p.line('x', 'y', color=colors[i], hover_color=colors[i], source=sources[i], **props) items.append((legend, [line])) name = names[i] line.name = name code_string += callback_string.format(name, str(i)) glyph_dict.update({name: line}) active.append(i) i += 1 l = Legend(items=items, location=(0, 0), orientation='horizontal', border_line_color=None) p.add_layout(l, 'below') hover = HoverTool(tooltips=[('date', '@date'), ('value', '@y{0.2f}')]) p.add_tools(hover) checkbox = CheckboxGroup(labels=labels, active=active, width=200) glyph_dict.update({'checkbox': checkbox}) checkbox.callback = CustomJS.from_coffeescript(args=glyph_dict, code=code_string) return checkbox, p #from bokeh.layouts import row # #c,p = create_plot('kcfs',df_list) #r=row([c,p]) ##doc = curdoc() ##doc.theme = theme ##doc.add_root(r) ## #show(r) ##
""" import numpy as np from bokeh.io import output_file, show from bokeh.layouts import row from bokeh.palettes import Viridis3 from bokeh.plotting import figure from bokeh.models import CheckboxGroup, CustomJS output_file("line_on_off.html", title="line_on_off.py example") p = figure() props = dict(line_width=4, line_alpha=0.7) x = np.linspace(0, 4 * np.pi, 100) l0 = p.line(x, np.sin(x), color=Viridis3[0], legend="Line 0", **props) l1 = p.line(x, 4 * np.cos(x), color=Viridis3[1], legend="Line 1", **props) l2 = p.line(x, np.tan(x), color=Viridis3[2], legend="Line 2", **props) checkbox = CheckboxGroup(labels=["Line 0", "Line 1", "Line 2"], active=[0, 1, 2], width=100) checkbox.callback = CustomJS(args=dict(l0=l0, l1=l1, l2=l2, checkbox=checkbox), lang="coffeescript", code=""" l0.visible = 0 in checkbox.active; l1.visible = 1 in checkbox.active; l2.visible = 2 in checkbox.active; """) layout = row(checkbox, p) show(layout)
def render_mask(img_path, document_objs, document_name='Default_Document', output_path='./outputs/', save_html=False): img = load_img(img_path) bokeh.plotting.reset_output() hover = HoverTool( tooltips=[ ("Name", "@name"), ("(x,y)", "($x, $y)"), ] ) fig = figure(tools=[hover, bokeh.models.WheelZoomTool(), bokeh.models.PanTool()]) fig.hover.point_policy = "follow_mouse" fig.sizing_mode='scale_width' fig.image_url(url=[os.path.basename(img_path)], x=0, y=img.shape[0], w=img.shape[1], h=img.shape[0]) script = "active = cb_obj.active;" labels = list(document_objs.keys()) color_map = bp.magma(len(labels)) args = dict() total_objs = 0 for key_id, key in enumerate(labels): xs = [] ys = [] for box in document_objs[key]: _ = np.array(box).reshape(-1,2).T xs.append(_[0]) ys.append(_[1]) ys=doccoor2planecoor(ys, img.shape[0]) data_source = dict(x=xs, y=ys, name=["%s %d"%(key, idx) for idx in range(len(xs))]) falpha = 0.5*int('table' not in key.lower()) lcolor = 'blue' if 'table' in key.lower() else 'red' lwidth = 3 if 'table' in key.lower() else 1 total_objs += len(document_objs[key]) args[key] = fig.patches('x', 'y', source=data_source, fill_color=color_map[key_id], fill_alpha=falpha, line_color=lcolor, line_width=lwidth, legend=key+': %d'%len(document_objs[key])) r = args[key] script += "\n%s.visible = active.includes(%d);"%(key, key_id) fig.patch([],[], fill_color='red', fill_alpha=0, line_color='white', legend='Total region: %d'%total_objs) fig.legend.click_policy="hide" fig.legend.location = "top_left" checkbox = CheckboxGroup(labels=labels, active=[])#active=[*range(len(labels))]) checkbox.callback = CustomJS(args=args, code=script) plt_obj = [fig] html = file_html(plt_obj, CDN, title=document_name) if save_html: base = os.path.join(output_path, document_name) if os.path.exists(base): shutil.rmtree(base) os.makedirs(base) shutil.copy(img_path, base) with open(os.path.join(base, 'main.html'),'w') as g: g.write(html) return html
def show_cells_on_stack(data, stack, X='X', Y='Y', channelNames=None, group='group', hue='hue', palette='Spectral11', default=0, alpha=.5, plot_width=500, plot_height=500, vmin=0, vmax=3, znorm=True): ''' stack: np. array of shape (nchannels,height,width) ''' from bokeh.models import CheckboxGroup, RadioButtonGroup, Legend, LegendItem if not channelNames: channelNames = ['Channel ' + str(i) for i in range(len(stack))] print(channelNames) s1 = ColumnDataSource(data=data) p1 = figure(plot_width=plot_width, plot_height=plot_height, tools="pan,wheel_zoom,reset") channels = {} for i, channel in enumerate(channelNames): img = stack[i] if znorm: img = (img - img.mean()) / img.std() channels[i] = p1.image(image=[img], x=[0], y=[0], dw=[plot_width], dh=[plot_height], color_mapper=LogColorMapper(palette=palette, low=vmin, high=vmax), global_alpha=alpha, visible=(i == default)) plots = {} #scaled_coordinates to fit in stack_dw,stack_dh dh_ratio = plot_height / img.shape[0] dw_ratio = plot_width / img.shape[1] data['warped_X'] = data[X] * dw_ratio data['warped_Y'] = data[Y] * dh_ratio groups = list(data[group].unique()) for g_id in groups: s2 = ColumnDataSource(data=data[data[group] == g_id]) scatter = p1.circle('warped_X', 'warped_Y', source=s2, alpha=1, color='hue') #,legend_label = str(g_id)) scatter.visible = False plots[g_id] = scatter select_celltype = CheckboxGroup(labels=groups, active=[], width=100) select_channel = RadioButtonGroup(labels=channelNames, active=default, width=100, orientation='horizontal') select_celltype.callback = CustomJS(args={ 'plots': plots, 'groups': groups, 'msel': select_celltype, 'fig': p1 }, code=""" //fig.title.text = 'new Title' for (var i =0; i<groups.length;i++){ plots[groups[i]].visible = msel.active.indexOf(i)>-1 } """) select_channel.callback = CustomJS(args={ 'channels': channels, 'sel': select_channel, 'fig': p1 }, code=""" //fig.title.text = Object.keys(channels).length.toString() for (var i =0; i<Object.keys(channels).length;i++){ channels[i].visible = false } var val = sel.active channels[val].visible = true /** if (val==0){ fig.title.text = 'confirm upper' chan1.visible = true chan2.visible = false //bg_channel = [z[0]] }else if (val==1){ fig.title.text = 'confirm lower' chan1.visible = false chan2.visible = true //bg_channel = [z[1]] } **/ """) layout = column(p1, select_celltype, select_channel) show(layout)
def create_charts(cur, include_8888=False): " Create chart on each tab for 5 regions + super DC " tables = create_tables(cur, for_div=True, include_8888=include_8888) tabs = [] for title, code, seats in regions: # chart components p = figure(x_axis_label='滾動日期', y_axis_label='支持%' if include_8888 else '有效支持%', x_axis_type="datetime") candids = [] earliest_date, latest_date = None, None for n in range(1, 30): candid, redness, trend = get_trend(cur, code, n, include_8888) if not trend: continue earliest_date, latest_date = trend[0][0], trend[-1][0] colour = getcolour(redness) line = p.line([d for d, _ in trend], [p for _, p in trend], color=colour, line_width=2, line_alpha=0.9) label = p.text([trend[-1][0]], [trend[-1][1]], text=[candid.rsplit(None, 1)[-1]], text_align='right', text_alpha=0.9, text_baseline='bottom', text_color=colour, text_font_size="9pt", y_offset=0.25) candids.append([n, candid, colour, trend[-1][1], line, label]) if include_8888: candid, redness, trend = get_trend(cur, code, 8888, True) if not trend: continue earliest_date, latest_date = trend[0][0], trend[-1][0] line = p.line([d for d, _ in trend], [p for _, p in trend], color="#707070", line_width=2, line_alpha=0.9) label = p.text([trend[-1][0]], [trend[-1][1]], text=["未決定"], text_align='right', text_alpha=0.9, text_baseline='bottom', text_color="#707070", text_font_size="9pt", y_offset=0.25) candids.append([8888, "未決定", "#707070", trend[-1][1], line, label]) p.line([earliest_date, latest_date], [100.0 / seats, 100.0 / seats], line_dash=[6, 3], color="black", line_width=2, line_alpha=0.5) # control all_lines = [n for n, _ in enumerate(candids)] top_5 = [ n for _, n in sorted([(c[3], n) for n, c in enumerate(candids)], reverse=True)[:5] ] for n in all_lines: if n not in top_5: candids[n][-1].visible = candids[n][-2].visible = False customjs_params = [("line%d"%n, candid[-2]) for n,candid in enumerate(candids)] + \ [("label%d"%n, candid[-1]) for n,candid in enumerate(candids)] jscode = ''' var lines = [%s]; var labels = [%s]; for (var n=0; n<lines.length; n++) { lines[n].visible = (cb_obj.active.indexOf(n) >= 0); labels[n].visible = (cb_obj.active.indexOf(n) >= 0); };''' % (",".join("line%d" % n for n in all_lines), ",".join( "label%d" % n for n in all_lines)) checkbox = CheckboxGroup(labels=[ str(800 + c[0] if code == 'SuperDC' else c[0]) + ' ' + c[1] + ' ' + ("%.2f%%" % c[3]) for c in candids ], active=top_5) checkbox.callback = CustomJS(args=dict(customjs_params), code=jscode) # ranking table div = Div(text=tables[code], width=600) # layout and show layout = row(checkbox, p, div) tab = Panel(child=layout, title=title + "民調(" + str(seats) + '席)') tabs.append(tab) # build and return return Tabs(tabs=tabs)
source_code = """ var inds = cb_obj.selected['1d'].indices; checkbox.active = inds; checkbox.change.emit() """ checkbox_code = """ source.selected['1d'].indices = cb_obj.active; """ button_code = """ console.log('checkbox',checkbox.active); console.log('source',source.selected['1d'].indices); """ source.callback = CustomJS(args=dict(checkbox=checkbox), code=source_code) checkbox.callback = CustomJS(args=dict(table=data_table, source=source), code=checkbox_code) button.callback = CustomJS(args=dict(table=data_table, checkbox=checkbox, source=source), code=button_code) show(widgetbox(data_table, checkbox, button))
checkbox.callback = CustomJS.from_coffeescript( args=dict(pc0=pc[0], pc1=pc[1], pc2=pc[2], pc3=pc[3], pc4=pc[4], pc5=pc[5], pc6=pc[6], pc7=pc[7], pc8=pc[8], pc9=pc[9], pc10=pc[10], pc11=pc[11], pc12=pc[12], pc13=pc[13], pc14=pc[14], pc15=pc[15], pc16=pc[16], pc17=pc[17], pc18=pc[18], pc19=pc[19], pc20=pc[20], pc21=pc[21], pc22=pc[22], pc23=pc[23], pc24=pc[24], pc25=pc[25], pc26=pc[26], checkbox=checkbox), code=''.join([ 'pc' + str(indp) + '.visible = ' + str(indp) + ' in checkbox.active;' for indp in range(27) ]))
def doc_maker(): ''' make the whole document ''' global spt_data, custom_path curdoc().clear() # removes everything in the current document # dropdown to select a spectrum select_spectrum = Select(title="Select a spectrum:", value='', options=[''] + sorted(os.listdir(spec_path)), name="select_spectrum", width=200) # textinput to give the full path to the location of spectra path_input = TextInput(title='Spectra folder', width=200, name="path_input") path_input.on_change('value', update_spec_path) # button to load the spectrum selected in the 'select_spectrum' dropdown load_button = Button(label='Load spectrum', width=200, css_classes=["custom_button"]) load_button.on_click(load_spectrum) if spt_data == {}: curdoc().add_root(widgetbox(path_input, select_spectrum, load_button)) if custom_path: path_input.value = custom_path else: path_input.value = spec_path return spectrum = spt_data['cur_spec'] header = spt_data[spectrum]['header'] species = np.array([spt_data[spectrum]['columns'][var] for var in header]) SZA = str(spt_data[spectrum]['sza']) zobs = str(spt_data[spectrum]['zobs']) freq = species[0] # the frequency list tm = species[1] # measured transmittance list tc = species[2] # calculated transmittance list cont = species[3] # continuum not_gas = 4 # number of column that are not retrieved species residuals = spt_data[spectrum]['resid'] # 100*(calculated - measured) sigma_rms = spt_data[spectrum]['rms_resid'] # sqrt(mean(residuals**2)) # spectrum figure fig = figure(name="spec_fig", title=spectrum + '; SZA=' + SZA + '; zobs=' + zobs + 'km; %resid=100*(Measured-Calculated); RMSresid=' + ('%.4f' % sigma_rms) + '%', plot_width=1000, plot_height=400, tools=TOOLS, y_range=Range1d(-0.04, 1.04), outline_line_alpha=0, active_inspect="crosshair", active_drag="box_zoom") # residual figure fig_resid = figure(name="resid_fig", plot_width=1000, plot_height=150, x_range=fig.x_range, tools=TOOLS, y_range=Range1d(-3, 3), outline_line_alpha=0, active_inspect="crosshair", active_drag="box_zoom") # axes labels fig_resid.xaxis.axis_label = u'Wavenumber (cm\u207B\u00B9)' fig_resid.yaxis.axis_label = '% Residuals' fig.yaxis.axis_label = 'Transmittance' #fig.xaxis.axis_label = u'Wavenumber (cm\u207B\u00B9)' for elem in [fig, fig_resid]: elem.yaxis.axis_label_text_font_size = "14pt" elem.yaxis.major_label_text_font_size = "13pt" elem.xaxis.axis_label_text_font_size = "14pt" elem.xaxis.major_label_text_font_size = "13pt" N_plots = list( range(len(species) - 2) ) # a range list from 0 to the number of plots, used by the checkbox group # group of checkboxes that will be used to toggle line and HoverTool visibility checkbox = CheckboxGroup(labels=header[3:] + ['Measured', 'Calculated'], active=N_plots, width=200) # plotting species lines plots = [] for j in range(len(species) - not_gas): try: plots.append( fig.line(x=freq, y=species[j + not_gas], color=colors[header[j + not_gas]], line_width=2, name=header[j + not_gas])) except KeyError: print( 'KeyError:', header[j + not_gas], 'is not specified in the "colors" dictionary, you need to add it with an associated color' ) sys.exit() # each line has a associated hovertool with a callback that looks at the checkboxes status for the tool visibility. fig.add_tools( HoverTool(mode='vline', line_policy='prev', renderers=[plots[j]], names=[header[j + not_gas]], tooltips=OrderedDict([('name', header[j + not_gas]), ('index', '$index'), ('(x;y)', '(@x{0.00} ; @y{0.000})') ]))) # adding the measured spectrum plots.append(fig.line(x=freq, y=tm, color='black', line_width=2, name='Tm')) fig.add_tools( HoverTool(mode='vline', line_policy='prev', renderers=[plots[j + 1]], names=['Tm'], tooltips=OrderedDict([('name', 'Measured'), ('index', '$index'), ('(x;y)', '(@x{0.00} ; @y{0.000})')]))) # adding the calculated spectrum plots.append( fig.line(x=freq, y=tc, color='chartreuse', line_width=2, name='Tc')) #fig.add_tools( HoverTool(mode='vline',line_policy='prev',renderers=[plots[j+2]],names=['Tc'],tooltips=OrderedDict( [('name','Calculated'),('index','$index'),('(x;y)','(@x{0.00} ; @y{0.000})')] )) ) # adding the continuum #plots.append(fig.line(x=freq,y=cont,color='#FF3399',line_dash='dashed',line_width=2,name='Cont')) #fig.add_tools( HoverTool(mode='vline',line_policy='prev',renderers=[plots[j+1]],names=['Cont'],tooltips=OrderedDict( [('name','Continuum'),('index','$index'),('(x;y)','(@x{0.00} ; @y{0.000})')] )) ) # legend outside of the figure fig_legend = Legend(items=[(header[j + not_gas], [plots[j]]) for j in range(len(species) - not_gas)] + [('Measured', [plots[-2]]), ('Calculated', [plots[-1]])], location=(0, 0), border_line_alpha=0) fig.add_layout(fig_legend, 'right') fig.legend.click_policy = "hide" fig.legend.inactive_fill_alpha = 0.6 # now the residual figure fig_resid.line(x=freq, y=residuals, color='black', name='residuals') fig_resid.line(x=freq, y=np.zeros(len(freq)), color='red') fig_resid.add_tools( HoverTool(mode='vline', line_policy='prev', names=['residuals'], tooltips={ 'index': '$index', '(x;y)': '($x{0.00} ; $y{0.000})' })) # set up a dummy legend for the residual figure so that it aligns with the spectrum figure dummy = fig_resid.line(x=freq, y=[0 for i in range(len(freq))], color='white', visible=False, alpha=0) fig_resid_legend = Legend(items=[(' ', [dummy])], location=(0, 0), border_line_alpha=0) fig_resid.add_layout(fig_resid_legend, 'right') # checkbox group callback checkbox_iterable = [('p' + str(i), plots[i]) for i in N_plots] + [('checkbox', checkbox)] checkbox_code = ''.join([ 'p' + str(i) + '.visible = checkbox.active.includes(' + str(i) + ');' for i in N_plots ]) checkbox.callback = CustomJS( args={key: value for key, value in checkbox_iterable}, code=checkbox_code) # button to uncheck all checkboxes clear_button = Button(label='Hide all lines', width=200) clear_button_code = """checkbox.active=[];""" + checkbox_code clear_button.callback = CustomJS( args={key: value for key, value in checkbox_iterable}, code=clear_button_code) # button to check all checkboxes check_button = Button(label='Show all lines', width=200) check_button_code = """checkbox.active=""" + str( N_plots) + """;""" + checkbox_code check_button.callback = CustomJS( args={key: value for key, value in checkbox_iterable}, code=check_button_code) # extension for the saved file name based on the path to spectra ext = custom_path.split(os.sep)[-2] # title div div = Div( text='<p align="center"><font size=4><b>{}</b></font></p>'.format(ext), width=fig.plot_width - 100) add_vlinked_crosshairs(fig, fig_resid) sub_grid = gridplot([[fig], [fig_resid], [div]], toolbar_location="left") # button to activate/deactivate hover tools hover_button = Button(label='Enable hover tools', button_type='success', width=200) hover_list = [i for i in sub_grid.select({"type": HoverTool})] # in 'comp' mode, each plot has a different hover tool, I hide them and this button will click them all at once. hover_button_code = """ if(cb_obj.button_type.includes("success")){ cb_obj.button_type = 'warning'; cb_obj.label = 'Disable hover tools' } else { cb_obj.button_type = 'success'; cb_obj.label= 'Enable hover tools'; } """ + ''.join([ "hover{}.active = !hover{}.active;".format(i, i) for i in range(len(hover_list)) ]) hover_button.callback = CustomJS( args={'hover{}'.format(i): elem for i, elem in enumerate(hover_list)}, code=hover_button_code) # put all the widgets in a box group = widgetbox(clear_button, check_button, hover_button, width=200, name="group") # the final grid for static plots grid = gridplot([[sub_grid, group]], toolbar_location=None) # save a standalone html document in spectra_app/save with open(os.path.join(save_path, '{}_{}.html'.format(spectrum, ext)), 'w') as outfile: outfile.write(file_html(grid, CDN, spectrum[:12] + spectrum[-3:])) group = widgetbox(clear_button, check_button, hover_button, path_input, select_spectrum, load_button, width=200) app_grid = gridplot([[sub_grid, group]], toolbar_location=None) # add that grid to the document curdoc().add_root(app_grid) if custom_path: path_input.value = custom_path else: path_input.value = spec_path
def bok_series(DATA, xlab='', ylab='', sup_title='', notes=''): """ the DICTIONARY (or OrderedDict) DATA must be of the form: DATA={ 'lab': { 'x':[...], 'y':[...], 'color':'red', }, 'lab2': { 'x':[...], 'y':[...], 'color':'red', }, 'lab3': { 'x':[...], 'y':[...], 'color':'red', }, etc... } if colors are not specified, the kelly_color dictionary will be used There will be a figure, and one "notes" text widget. The main figure will show all the time series - 'xlab' is the label of the x axis - 'ylab' is the label of the y axis - 'sup_title' is the header of the html page - 'notes' is a string of html code that will be displayed in a text widget beside the plots """ ######## /!\ important time handling to make sure times don't get shifted from UTC due to computer environment variables when using datetime objects ########### os.environ['TZ'] = 'UTC' time.tzset() # This will not change your system timezone settings ################################################################################################################################################################ for label in DATA: try: test = DATA[label]['color'] except KeyError: DATA[label]['color'] = kelly_colors[kelly_colors.keys()[ DATA.keys().index(label)]] DATA[label]['nicetime'] = [ dat.strftime('%d-%m-%Y %H:%M:%S') for dat in DATA[label]['x'] ] if sup_title == '': sup_title = """<font size="4">Use the "Box Select" tool to select data of interest.</br>The table shows statistics between each dataset and the data shown in black.</font></br></br>""" header = Div(text=sup_title, width=700) # the title of the dashboard sources = {} # data sources for the main figure for label in DATA: sources[label] = ColumnDataSource(data={ key: DATA[label][key] for key in DATA[label] if key != 'color' }) # 'label' data #get the min and max of all the data x and y min_x = min([DATA[label]['x'][0] for label in DATA]) max_x = max([DATA[label]['x'][-1] for label in DATA]) min_y = min([min(abs(DATA[label]['y'])) for label in DATA]) max_y = max([max(DATA[label]['y']) for label in DATA]) max_ampli = max_y - min_y # we will set the y axis range at +/- 10% of the data max amplitude min_y = min_y - max_ampli * 10 / 100 max_y = max_y + max_ampli * 10 / 100 TOOLS = "pan,hover,wheel_zoom,box_zoom,undo,redo,reset,save" # interactive tools available in the html plot fig = figure(output_backend="webgl", plot_width=900, plot_height=200 + 20 * (len(DATA.keys()) - 2), tools=TOOLS, x_axis_type='datetime', y_range=[min_y, max_y], toolbar_location='left') # figure with the time series # actual time series plots = [] for label in DATA: plots.append( fig.scatter(x='x', y='y', color=DATA[label]['color'], alpha=0.5, source=sources[label])) # hover tool configuration fig.select_one(HoverTool).tooltips = [ ('index', '$index'), ('y', '@y'), ('x', '@nicetime'), ] N_plots = range(len(plots)) # used in the checkbox callbacks # setup the legend and axis labels for the main figure legend = Legend(items=[(DATA.keys()[i], [plots[i]]) for i in range(len(DATA.keys()))], location=(0, 0)) fig.add_layout(legend, 'right') fig.yaxis.axis_label = ylab fig.xaxis.axis_label = xlab checkbox = CheckboxGroup( labels=DATA.keys(), active=range(len(DATA.keys())), width=100) # the group of checkboxes, one for each 'label' in DATA iterable = [('p' + str(i), plots[i]) for i in N_plots] + [ ('checkbox', checkbox) ] # associate each element needed in the callback to a string # checkboxes to trigger line visibility checkbox_iterable = [('p' + str(i), plots[i]) for i in N_plots] + [('checkbox', checkbox)] checkbox_code = ''.join([ 'p' + str(i) + '.visible = checkbox.active.includes(' + str(i) + ');' for i in N_plots ]) checkbox.callback = CustomJS( args={key: value for key, value in checkbox_iterable}, code=checkbox_code) # button to uncheck all checkboxes clear_button = Button(label='Clear all', width=120) clear_button_code = """checkbox.active=[];""" + checkbox_code clear_button.callback = CustomJS( args={key: value for key, value in checkbox_iterable}, code=clear_button_code) # button to check all checkboxes check_button = Button(label='Check all', width=120) check_button_code = """checkbox.active=""" + str( N_plots) + """;""" + checkbox_code check_button.callback = CustomJS( args={key: value for key, value in checkbox_iterable}, code=check_button_code) # default text of the 'info' Div widget if notes == '': notes = """ <font size="5"><b>Notes:</b></font></br></br> <font size="2"> </font> """ dumdiv = Div( text='', width=50 ) # dummy div widget to force a padding between gridplot elements info = Div(text=notes, width=400, height=300) # the information Div widget group = widgetbox( checkbox, clear_button, check_button ) # group the checkboxes and buttons in a common "widget box" sub_grid = gridplot([[fig, group], [info]], toolbar_location='left') # put everything in a grid grid = gridplot( [[header], [sub_grid]], toolbar_location=None ) # put the previous grid under the 'header' Div widget, this is done so that the toolbar of sub_grid won't appear on the side of the header. return grid
def bok_comp(DATA, select, xlab='', ylab='', sup_title='', notes='', prec='2'): """ If you have data 'select' to which you want to compare several datasets 'lab0', 'lab1', etc. the DICTIONARY (or OrderedDict) DATA must be of the form: DATA={ 'lab': { 'lab0':{x':[...],'y':[...]}, 'select0':{x':[...],'y':[...]}, 'color':'red', }, 'lab1': { 'lab1':{x':[...],'y':[...]}, 'select1':{x':[...],'y':[...]}, 'color':'blue', }, 'lab2': { 'lab2':{x':[...],'y':[...]}, 'select2':{x':[...],'y':[...]}, 'color':'orange', }, etc... } where 'select0', 'select1' etc are subsets of 'select' containing only data respectively coincident with 'lab0','lab1' etc. if colors are not specified, the kelly_color dictionary will be used There will be two figures, one table, and one "notes" text widget. The main figure will show all the time series of coincident data between each 'lab' and 'select'. The second figure will show a correlation plot including only data selected within the BoxSelect tool. The data in the table will also be updated based on the data selected within the BoxSelect tool. - 'select' is the name of the common data to which all dataset will be compared - 'xlab' is the label of the x axis - 'ylab' is the label of the y axis - 'sup_title' is the header of the html page - 'notes' is a string of html code that will be displayed in a text widget beside the plots - 'prec' is a "number string" that sets the precision of the values displayed in the table """ ######## /!\ important time handling to make sure times don't get shifted from UTC due to computer environment variables when using datetime objects ########### os.environ['TZ'] = 'UTC' time.tzset() # This will not change your system timezone settings ################################################################################################################################################################ for label in DATA: try: test = DATA[label]['color'] except KeyError: DATA[label]['color'] = kelly_colors[kelly_colors.keys()[ DATA.keys().index(label)]] for key in DATA[label]: if key != 'color': DATA[label][key]['nicetime'] = [ dat.strftime('%d-%m-%Y %H:%M:%S') for dat in DATA[label][key]['x'] ] if sup_title == '': sup_title = """<font size="4">Use the "Box Select" tool to select data of interest.</br>The table shows statistics between each dataset and the data shown in black.</font></br></br>""" header = Div(text=sup_title, width=700) # the title of the dashboard # list of columns for the table columns = [ TableColumn(field='Name', title='Name'), TableColumn(field='N', title='N'), TableColumn(field='RMS', title='RMS'), TableColumn(field='Bias', title='Bias'), TableColumn(field='Scatter', title='Scatter'), TableColumn(field='R', title='R'), ] temp = [ 0 for i in DATA ] # dummy list to initially fill teh columns of the table with zeros table_source = ColumnDataSource( data={ 'Name': DATA.keys(), 'N': temp, 'RMS': temp, 'Bias': temp, 'Scatter': temp, 'R': temp }) # the data source of the table data_table = DataTable(source=table_source, columns=columns, width=450, height=45 * len(DATA.keys())) # the table itself txt = Div( text= 'Display the table with increasing # before starting a new selection', width=450 ) # text div that will be updated with the selected range of date within the BoxSelect tool sources = {} # data sources for the main figure cor_sources = {} # data sources for the correlation figure count = 0 # iterated in the for loop below and used in the sources callbacks for label in DATA: sources[label] = { } # for each source 'label', there will be two time series, the 'label' data, and the coincident 'select' data sources[label][label] = ColumnDataSource( data=DATA[label][label]) # 'label' data sources[label][select] = ColumnDataSource( data=DATA[label] [select]) # 'select' data that is coincident with 'label' data cor_sources[label] = ColumnDataSource(data={ 'x': [], 'y': [] }) # fillable source for the correlation figure # give a callback to all 'label' data sources to update the correlation plot and the table based on the BoxSelect tool selection. sources[label][label].callback = CustomJS( args=dict(s2=sources[label][select], dt=data_table, scor=cor_sources[label]), code=""" var inds = cb_obj.selected['1d'].indices; var d1 = cb_obj.data; var d2 = s2.data; var tab = dt.source.data; var dcor = scor.data; var difm = 0; var difm2 = 0; var scat = 0; var ym1 = 0; var ym2 = 0; var T1 = 0; var T2 = 0; var T3 = 0; dcor['x'] = []; dcor['y'] = []; tab['N'][""" + str(count) + """] = inds.length; if (inds.length == 0) { tab['RMS'][""" + str(count) + """] = 0; tab['Bias'][""" + str(count) + """] = 0; tab['Scatter'][""" + str(count) + """] = 0; tab['R'][""" + str(count) + """] = 0; dt.change.emit(); return; } for (i=0; i < inds.length; i++){ difm += d1['y'][inds[i]] - d2['y'][inds[i]]; difm2 += Math.pow(d1['y'][inds[i]] - d2['y'][inds[i]],2); ym1 += d1['y'][inds[i]]; ym2 += d2['y'][inds[i]]; dcor['x'].push(d2['y'][inds[i]]); dcor['y'].push(d1['y'][inds[i]]); } difm /= inds.length; difm2 /= inds.length; ym1 /= inds.length; ym2 /= inds.length; for (i=0; i < inds.length; i++){ scat += Math.pow(d1['y'][inds[i]] - d2['y'][inds[i]] - difm,2); } for (i=0; i < inds.length; i++){ T1 += (d1['y'][inds[i]] - ym1)*(d2['y'][inds[i]] - ym2); T2 += Math.pow(d1['y'][inds[i]] - ym1,2); T3 += Math.pow(d2['y'][inds[i]] - ym2,2); } tab['RMS'][""" + str(count) + """] = Math.sqrt(difm2).toFixed(""" + prec + """); tab['Bias'][""" + str(count) + """] = difm.toFixed(""" + prec + """); tab['Scatter'][""" + str(count) + """] = Math.sqrt(scat/(inds.length -1)).toFixed(""" + prec + """); tab['R'][""" + str(count) + """] = (T1/Math.sqrt(T2*T3)).toFixed(""" + prec + """); dt.change.emit(); scor.change.emit(); """) count += 1 #get the min and max of all the data x and y min_x = min([DATA[label][label]['x'][0] for label in DATA]) max_x = max([DATA[label][label]['x'][-1] for label in DATA]) min_y = min([min(abs(DATA[label][label]['y'])) for label in DATA]) max_y = max([max(DATA[label][label]['y']) for label in DATA]) max_ampli = max_y - min_y # we will set the y axis range at +/- 10% of the data max amplitude min_y = min_y - max_ampli * 10 / 100 max_y = max_y + max_ampli * 10 / 100 TOOLS = "pan,hover,wheel_zoom,box_zoom,undo,redo,reset,box_select,save" # interactive tools available in the html plot fig = figure(output_backend="webgl", plot_width=900, plot_height=200 + 20 * (len(DATA.keys()) - 2), tools=TOOLS, x_axis_type='datetime', y_range=[min_y, max_y], toolbar_location='left') # figure with the time series fig.tools[ -2].dimensions = 'width' # only allow the box select tool to select data along the X axis (will select all Y data in a given X range) UTC_offset = str( (datetime(*time.gmtime()[:6]) - datetime(*time.localtime()[:6])).seconds ) # machine UTC offset, javascript "Date" in the callback seems to offset the time data by the UTC offset # make the BoxSelect tool update the 'txt' Div widget with the currently selected range of dates. fig.tools[-2].callback = CustomJS(args=dict(txt=txt), code=""" var sel = cb_data["geometry"]; var startsec = sel["x0"]/1000; var start = new Date(0); start.setUTCSeconds(startsec) var startstring = ("0" + start.getUTCDate()).slice(-2) + "-" + ("0"+(start.getUTCMonth()+1)).slice(-2) + "-" +start.getUTCFullYear() + " " + ("0" + start.getUTCHours()).slice(-2) + ":" + ("0" + start.getUTCMinutes()).slice(-2); var finishsec = sel["x1"]/1000; var finish = new Date(0); finish.setUTCSeconds(finishsec) var finishstring = ("0" + finish.getUTCDate()).slice(-2) + "-" + ("0"+(finish.getUTCMonth()+1)).slice(-2) + "-" +finish.getUTCFullYear() + " " + ("0" + finish.getUTCHours()).slice(-2) + ":" + ("0" + finish.getUTCMinutes()).slice(-2); txt.text = 'Selection range from '+startstring + ' to ' + finishstring; txt.trigger("change"); """) # actual time series plots = [] for label in DATA: plots.append( fig.scatter(x='x', y='y', color=DATA[label]['color'], alpha=0.5, source=sources[label][label])) plots.append( fig.scatter(x='x', y='y', color='black', alpha=0.5, source=sources[label][select])) # hover tool configuration fig.select_one(HoverTool).tooltips = [ ('index', '$index'), ('y', '@y'), ('x', '@nicetime'), ] N_plots = range(len(plots)) # used in the checkbox callbacks # used in the checkbox callbacks, we will trigger both 'label' and coincident 'select' data visibility at the same time N_plots2 = range(len(plots) / 2) even_plots = [i for i in N_plots if i % 2 == 0] # setup the legend and axis labels for the main figure legend = Legend(items=[(select, [plots[1]])] + [(DATA.keys()[i], [plots[even_plots[i]]]) for i in range(len(DATA.keys()))], location=(0, 0)) fig.add_layout(legend, 'right') fig.yaxis.axis_label = ylab fig.xaxis.axis_label = xlab # correlation figure cor_fig = figure(output_backend="webgl", title='Correlations', plot_width=250, plot_height=280, x_range=[min_y, max_y], y_range=[min_y, max_y]) cor_fig.toolbar.logo = None cor_fig.toolbar_location = None cor_fig.xaxis.axis_label = ' '.join([select, ylab]) cor_fig.yaxis.axis_label = ylab # one to one line in the correlation plot linerange = list(np.arange(0, int(max_y), ceil(max_y) / 10.0)) + [max_y] cor_fig.line(x=linerange, y=linerange, color='black') # actual correlation plots corplots = [] for label in DATA: corplots.append( cor_fig.scatter(x='x', y='y', color=DATA[label]['color'], alpha=0.5, source=cor_sources[label])) N_corplots = range(len(corplots)) # used in the checkbox callbacks checkbox = CheckboxGroup( labels=DATA.keys(), active=range(len(DATA.keys())), width=100) # the group of checkboxes, one for each 'label' in DATA iterable = [('p' + str(i), plots[i]) for i in N_plots] + [ ('pcor' + str(i), corplots[i]) for i in N_corplots ] + [('checkbox', checkbox) ] # associate each element needed in the callback to a string # checkboxes to trigger line visibility checkbox_code = """var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };""" checkbox_code += ''.join([ 'p' + str(i) + '.visible = indexOf.call(checkbox.active, ' + str(i / 2) + ') >= 0; p' + str(i + 1) + '.visible= indexOf.call(checkbox.active, ' + str(i / 2) + ') >= 0; pcor' + str(i / 2) + '.visible = indexOf.call(checkbox.active, ' + str(i / 2) + ') >= 0;' for i in range(0, len(N_plots), 2) ]) checkbox.callback = CustomJS(args={key: value for key, value in iterable}, code=checkbox_code) # button to uncheck all checkboxes clear_button = Button(label='Clear all', width=100) clear_button_code = """checkbox.set("active",[]);""" + checkbox_code clear_button.callback = CustomJS( args={key: value for key, value in iterable}, code=clear_button_code) # button to check all checkboxes check_button = Button(label='Check all', width=100) check_button_code = """checkbox.set("active",""" + str( N_plots2) + """);""" + checkbox_code check_button.callback = CustomJS( args={key: value for key, value in iterable}, code=check_button_code) # button to save the table data in a .csv file download_button = Button(label='Save Table to CSV', width=100) download_button.callback = CustomJS(args=dict(dt=data_table), code=""" var tab = dt.source.data; var filetext = 'Name,N,RMS,Bias,Scatter,R'+String.fromCharCode(10); for (i=0; i < tab['Name'].length; i++) { var currRow = [tab['Name'][i].toString(), tab['N'][i].toString(), tab['RMS'][i].toString(), tab['Bias'][i].toString(), tab['Scatter'][i].toString(), tab['R'][i].toString()+String.fromCharCode(10)]; var joined = currRow.join(); filetext = filetext.concat(joined); } var filename = 'data_result.csv'; var blob = new Blob([filetext], { type: 'text/csv;charset=utf-8;' }); var link = document.createElement("a"); link = document.createElement('a') link.href = URL.createObjectURL(blob); link.download = filename link.target = "_blank"; link.style.visibility = 'hidden'; link.dispatchEvent(new MouseEvent('click')) """) # default text of the 'info' Div widget if notes == '': notes = """ <font size="5"><b>Notes:</b></font></br></br> <font size="2"> Use the "Box Select" tool to select data of interest.</br> The table shows statistics between each dataset and the data shown in black. </font> """ dumdiv = Div( text='', width=50 ) # dummy div widget to force a padding between gridplot elements info = Div(text=notes, width=400, height=300) # the information Div widget group = widgetbox( checkbox, clear_button, check_button ) # group the checkboxes and buttons in a common "widget box" table_grid = gridplot( [[txt], [download_button], [data_table]], toolbar_location=None ) # group together the table, txt Div widget, and download button sub_grid = gridplot([[fig, group], [cor_fig, table_grid, dumdiv, info]], toolbar_location='left') # put everything in a grid grid = gridplot( [[header], [sub_grid]], toolbar_location=None ) # put the previous grid under the 'header' Div widget, this is done so that the toolbar of sub_grid won't appear on the side of the header. return grid