diff --git a/__init__.py b/__init__.py index 734a9a0..aca6d62 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,3 @@ from .models import ActiveExplore +from .apps import ExploreML from .data import loadActiveH5 diff --git a/apps/__init__.py b/apps/__init__.py new file mode 100644 index 0000000..a437956 --- /dev/null +++ b/apps/__init__.py @@ -0,0 +1 @@ +from .explore_predict import ExploreML diff --git a/apps/explore_predict.py b/apps/explore_predict.py new file mode 100644 index 0000000..f0c181a --- /dev/null +++ b/apps/explore_predict.py @@ -0,0 +1,47 @@ +from exploreML.models import ActiveExplore +from exploreML.models import PredictMap +from bokeh.layouts import column, row + +class ExploreML: + + + def __init__(self,grid_shape=(1,1),**kwargs): + + self.grid_shape = grid_shape + self.layouts = [] + #maybe map_p_high/low + + + def addActiveExplore(self,data_dict,sampling_dict,**kwargs): + + active = ActiveExplore(data_dict, sampling_dict, **kwargs) + + self.layouts += [active.layout] + self.toolbar = active.toolbar + self.clust_dict = active.clust_dict # Index clusters for heatmap + self.clust_methods = active.clust_methods + self.upper_source = active.upper_source + self.upper_dict = active.upper_dict + self.row_name = active.row_name + self.col_name = active.col_name + self.sample_sliders = active.sample_sliders + self.plot_size = active.plot_size + self.radio_button_group = active.radio_button_group + self.toggle = active.toggle + self.data_toggle = active.data_toggle + self.active_dim = active.active_dim + + + def addPredictMap(self,pred_df,map_dict,**kwargs): + + predict = PredictMap(pred_df, map_dict, toolbar=self.toolbar, + clust_dict=self.clust_dict,clust_methods=self.clust_methods, + upper_source=self.upper_source,upper_dict=self.upper_dict, + row_name=self.row_name,col_name=self.col_name, + radio_button_group=self.radio_button_group,toggle=self.toggle, + sample_sliders=self.sample_sliders,plot_size=self.plot_size, + data_toggle=self.data_toggle,active_dim=self.active_dim,**kwargs) + self.layouts += [predict.layout] + + def Layout(self): + self.layout = row(*self.layouts) diff --git a/models/__init__.py b/models/__init__.py index f98ae13..8f70103 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1 +1,2 @@ from .active_explore import ActiveExplore +from .predict_map import PredictMap diff --git a/models/active_explore.py b/models/active_explore.py index f2a9ca2..cd6e8d1 100644 --- a/models/active_explore.py +++ b/models/active_explore.py @@ -2,15 +2,15 @@ import pandas as pd import seaborn as sns import math -import os from bokeh.io import show, save from bokeh.plotting import Figure, output_file, show, output_notebook from bokeh.layouts import column, row from bokeh.models import ColorBar, LinearColorMapper, BasicTicker, CustomJS, ColumnDataSource,\ Toggle, Slider, RadioButtonGroup, Select, Legend, ColorPicker, Panel, Tabs, RangeSlider,HoverTool +from bokeh.models.widgets import Div from bokeh.palettes import all_palettes from scipy.cluster.hierarchy import linkage, dendrogram -from exploreML.models.custom_tools import ResetTool +import itertools with open('exploreML/models/active_explore_js/slider_callback.js','r') as f: slider_callback_js = f.read() @@ -45,15 +45,15 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, row_name='dim1', col_name='dim2', val_name='entry_value',hide_heatmap_labels=False, heatmap_colors = 'default', n_colors=10, inds_colors=[1,2,4,5,6,7,8], color_palette='Category10',plot_size=700, line_width=500, line_height=300, - name='active explorer', url='active_explorer.html', plot_location='below'): + name='active explorer', url='active_explorer.html', plot_location='below', + file_output=True): self.name = name self.numLinePlots = num_line_plots self.is_sym = is_sym - - # Output file - output_file(filename=url, title=name) - # output_notebook() + self.row_name = row_name + self.col_name = col_name + self.plot_size = plot_size # Load data M = data_dict['M'] @@ -81,6 +81,7 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, sampler_color = dict(zip(sampling_names, sampler_palette[:n_samplers])) active_dim = sampling_dfs[0].shape[0] + self.active_dim = active_dim if self.is_sym: sampling_methods = [self._makeSymAL(sampler, row_coord, col_coord, active_x, batch_col)\ for sampler in sampling_dfs] @@ -94,6 +95,7 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, # Precompute the indices for the different clustering methods clust_dict = {method:self._makeClustIndex(M, method) for method in clust_methods} self.clust_dict = clust_dict + self.clust_methods = clust_methods # Active learning data sampling_data = {sample:self._addIndexCols(df, df_index, df_cols, meta_vars)\ @@ -108,6 +110,7 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, # Column source data of mask for each clustering method upper_dict = {method:self._makeGImatrix(self._makeMaskUpper(M.iloc[clust_dict[method][0],clust_dict[method][1]]),meta_vars)\ .dropna(axis=0).to_dict(orient='list') for method in clust_methods} + self.upper_dict = upper_dict # Collecting the quantitative columns from the sampling data quant_options = sampling_methods[0].describe().T.query('std > 0').index.to_list() @@ -121,6 +124,7 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, for sampler,data in sampling_data.items()} upper_source = ColumnDataSource(data=upper_dict[init_clust]) + self.upper_source = upper_source # Initialize sources # GI column source data @@ -235,19 +239,14 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, with open('exploreML/models/active_explore_js/radio_call.js','r') as f: radio_call_js = f.read() - radio_call = CustomJS(args=dict(methods=clust_methods,clust_dict=clust_dict,plot=p, - up_source=upper_source, up_dict=upper_dict),code=radio_call_js) - - sample_sliders = {sampler:Slider(start=0, end=active_dim, value=0, step=1,\ + # TODO: Make step a variable + sample_sliders = {sampler:Slider(start=0, end=active_dim, value=0, step=120,\ title=title,max_width=300)\ for sampler,title in zip(sampling_names,sampling_titles)} - radio_button_group = RadioButtonGroup(labels=[x.capitalize() for x in clust_methods], active=0,max_width=300) - slider_js = {sampler:sample_sliders[sampler].js_on_change('value', self._slider_callback(sampler, active_sources, sampling_sources, symMult))\ for sampler in sampling_names} - - radio_button_group.js_on_click(radio_call) + self.sample_sliders = sample_sliders toggle = Toggle(label="Lower Triangle (Toggle)", button_type="primary",max_width=300) toggle.js_on_click(CustomJS(args=dict(plot=upperFig),code=""" @@ -256,6 +255,7 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, plot.visible = cb_obj.active; """)) + self.toggle = toggle #default, primary, success, warning, danger, light #Toggle to show and hide training examples @@ -270,6 +270,7 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, data_toggle_js = f.read() data_toggle = Toggle(label="Show All (Toggle)", button_type="primary",max_width=300) + self.data_toggle = data_toggle data_toggle.js_on_click(CustomJS(args=dict(sliders=sample_sliders, active_dim=\ active_dim), code=data_toggle_js)) @@ -282,19 +283,32 @@ def __init__(self, data_dict, sampling_dict, is_sym=False, num_line_plots=2, train_picker = ColorPicker(width=50,color='white') train_picker.js_link('color', trainFig.glyph, 'fill_color') - sliders = column(list(sample_sliders.values())) + sliders = column(list(sample_sliders.values()),name='slider_col') linePlots = [column(*line) for line in list(zip(line_selects,line_tabs))] - heatmap_layout = row(column(radio_button_group,toggle,sliders,data_toggle, - row(train_toggle,train_picker),range_slider,select_colorbar,width=340), - p) + radio_call = CustomJS(args=dict(methods=clust_methods,clust_dict=clust_dict,plot=p, + up_source=upper_source, up_dict=upper_dict),code=radio_call_js) + + radio_button_group = RadioButtonGroup(labels=[x.capitalize() for x in clust_methods], active=0, max_width=300) + radio_button_group.js_on_click(radio_call) + self.radio_button_group = radio_button_group + + toolbar = column(radio_button_group,toggle,sliders,data_toggle, + row(train_toggle,train_picker), + range_slider,select_colorbar, + width=310,name='toolbar', margin=(0,10,0,10)) + heatmap_layout = row(toolbar,p) + + layout = self._make_layout(heatmap_layout,linePlots,plot_location) + + self.toolbar = toolbar + self.layout = layout - layout = self._make_layout(heatmap_layout,linePlots, plot_location) + if file_output: - # show(layout) - # self.layout = layout - save(layout) + output_file(filename=url, title=name) + save(layout) def _make_layout(self, heatmap_layout, linePlots, plot_location): @@ -313,7 +327,8 @@ def _make_layout(self, heatmap_layout, linePlots, plot_location): layout = column(line_layout, heatmap_layout) elif plot_location == 'below': # Below - layout = column(heatmap_layout,line_layout) + # layout = column(heatmap_layout,Div(height=80),line_layout) + layout = column(heatmap_layout,Div(height=20),line_layout) return layout @@ -331,32 +346,8 @@ def _line_callback(self, Fig, Fig2, Figs1, Figs2, samplerCol_meta): plot2=Figs2, col_meta=samplerCol_meta, yaxis=Fig.yaxis[0], - yaxis2=Fig2.yaxis[0]), code=""" - console.log('select: value=' + this.value, this.toString()) - - var select = cb_obj.value; - const keys = Object.keys(plot); - var keysLength = keys.length; - - for (var i = 0; i < keysLength; i++) { - plot[keys[i]].glyph.y.field = select; - plot[keys[i]].data_source.change.emit(); - - plot2[keys[i]].glyph.y.field = select; - plot2[keys[i]].data_source.change.emit(); - } - - var mx = col_meta[select].max + col_meta[select].max * 0.05; - var mn = col_meta[select].min - col_meta[select].max * 0.05; - - fig.y_range.end = mx; - fig.y_range.start = mn; - yaxis.axis_label = select; - - fig2.y_range.end = mx; - fig2.y_range.start = mn; - yaxis2.axis_label = select; - """) + yaxis2=Fig2.yaxis[0]), + code=line_callback_js) return callback diff --git a/models/active_explore_js/line_callback.js b/models/active_explore_js/line_callback.js index c57f8fa..2b5ceb4 100644 --- a/models/active_explore_js/line_callback.js +++ b/models/active_explore_js/line_callback.js @@ -12,10 +12,13 @@ for (var i = 0; i < keysLength; i++) { plot2[keys[i]].data_source.change.emit(); } -fig.y_range.end = col_meta[select].max; -fig.y_range.start = col_meta[select].min; +var mx = col_meta[select].max + col_meta[select].max * 0.05; +var mn = col_meta[select].min - col_meta[select].max * 0.05; + +fig.y_range.end = mx; +fig.y_range.start = mn; yaxis.axis_label = select; -fig2.y_range.end = col_meta[select].max; -fig2.y_range.start = col_meta[select].min; +fig2.y_range.end = mx; +fig2.y_range.start = mn; yaxis2.axis_label = select; diff --git a/models/active_explore_js/radio_call_plots.js b/models/active_explore_js/radio_call_plots.js new file mode 100644 index 0000000..fb02ce0 --- /dev/null +++ b/models/active_explore_js/radio_call_plots.js @@ -0,0 +1,43 @@ +console.log('radio_button_group: active=' + this.active, this.toString()) + +//Store radioVal for clustering methods +let radioVal = cb_obj.active; + +let method = methods[radioVal]; +let x_range = clust_dict[method][3]; +let y_range = [].concat(clust_dict[method][2]).reverse(); + +plot1.x_range.factors = x_range; +plot1.y_range.factors = y_range; + +plot2.x_range.factors = x_range; +plot2.y_range.factors = y_range; + +let cdsLists = up_dict[method]; + +//Reorder mask +//Assign mask source to variable +var data = up_source.data; +var keys = Object.keys(data); +var keysLength = keys.length; + +// Reorder mask +var mask_map = new Map(); +var new_map = new Map(); +for (var i = 0; i < keysLength; i++) { + + //Assign mask source to map variable + mask_map.set(keys[i], data[keys[i]]); + + // Reassign CDS + new_map.set(keys[i], cdsLists[keys[i]]); + + // Clear old mask CDS + mask_map.get(keys[i]).splice(0, mask_map.get(keys[i]).length); + + // Add new mask cds + mask_map.set(keys[i], mask_map.get(keys[i]).push(...new_map.get(keys[i]))); + +} + +up_source.change.emit(); diff --git a/models/predict_map.py b/models/predict_map.py new file mode 100644 index 0000000..999df3d --- /dev/null +++ b/models/predict_map.py @@ -0,0 +1,292 @@ +import numpy as np +import pandas as pd +import seaborn as sns +from holoviews.plotting.util import process_cmap +from bokeh.layouts import column, row +from bokeh.models import ColorBar, LogColorMapper, LinearColorMapper, ColumnDataSource, RadioGroup, RadioButtonGroup,\ + BasicTicker, HoverTool, Div, CustomJS +from bokeh.plotting import Figure +from itertools import count + +class PredictMap: + + """ + Args: + pred_df: + pred_dict: + """ + + def __init__(self, pred_df, predmap_dict, toolbar=None, predmap_init_field='prediction', mapdictInit='random', + map_q_low=0,map_q_high=100,clust_dict=None,clust_methods=None,init_clust='None', + upper_source=None,upper_dict=None,constant_map=['density'],radio_button_group=None, + row_name='dim1',col_name='dim2',sample_sliders=None,hide_heatmap_labels=True, + plot_size=400,is_sym=True,toggle=None,data_toggle=None,active_dim=None): + + pred_df = self._addSym(pred_df,True,True) + map_dict = {} + for key1,val1 in predmap_dict.items(): + map_dict[key1] = {} + for key2 in val1.keys(): + map_dict[key1][key2] = self._addSym(predmap_dict[key1][key2],False,False) + + predmap_labels = ["prediction", "uncertainty", "density", "similarity"] + + # Create predmap datasource + # Assign column for entry color on prediction maps + predmap_ds = map_dict[mapdictInit][predmap_init_field] + pred_df_init = pred_df.assign(entry_color=predmap_ds.loc[:,'0'].values)\ + .assign(**{k:v.loc[:,'0'].values for k,v in map_dict[mapdictInit].items()}) + pred_ds = ColumnDataSource(pred_df_init) + + # Create lists/dicts for color palettes and labels + slider_labels_dict = dict(zip(sample_sliders.keys(),count())) + slider_key_dict = dict(enumerate(sample_sliders.keys())) + + heatmap_colors = sns.diverging_palette(260, 10, n=256).as_hex() + heatmap_colors2 = process_cmap('cet_CET_L17') + + pred_palette = heatmap_colors + uncertainty_palette,density_palette,similarity_palette = [heatmap_colors2]*3 + predmap_labels_dict = dict(zip(predmap_labels,count())) + predmap_key_dict = dict(enumerate(predmap_labels)) + predmap_palettes = [pred_palette,uncertainty_palette,density_palette,similarity_palette] + predmap_palette_dict = dict(zip(predmap_labels,predmap_palettes)) + + map_bounds = {k:[np.percentile(v.values,map_q_low),np.percentile(v.values,map_q_high)]\ + for k,v in map_dict[mapdictInit].items()} + map_bounds['prediction'] = [-0.3,0.3] + + map_bounds = ColumnDataSource(map_bounds) + + predmap_mapper = LinearColorMapper(palette=predmap_palette_dict[predmap_init_field], + low=map_bounds.data[predmap_init_field][0], + high=map_bounds.data[predmap_init_field][1]) + + TOOLS = "save, box_zoom, reset" + p3 = Figure(title='Prediction Heatmap',tools=TOOLS, + frame_width=plot_size, frame_height=plot_size, + output_backend="webgl",match_aspect =True,toolbar_location='above', + x_range=clust_dict[init_clust][3],y_range=clust_dict[init_clust][2][::-1]) + + p3.outline_line_color = None + p3.grid.grid_line_color = None + p3.axis.axis_line_color = None + p3.axis.major_tick_line_color = None + p3.xaxis.axis_label = col_name + p3.yaxis.axis_label = row_name + + if hide_heatmap_labels: + p3.xaxis.major_label_text_font_size = '0pt' # preferred method for removing tick labels + p3.yaxis.major_label_text_font_size = '0pt' + else: + p3.axis.major_label_text_font_size = "7px" + p3.axis.major_label_standoff = 0 + p3.xaxis.major_label_orientation = np.pi/3 + + predmap_color_bar = ColorBar(color_mapper=predmap_mapper, major_label_text_font_size="10px", + major_tick_line_color='black', + location=(0,0), ticker=BasicTicker(), + width=15,label_standoff=7) + + p3.add_layout(predmap_color_bar, "right") + + heatFig = p3.rect(x=col_name, y=row_name, width=1,height=1, + source=pred_ds, + color={'field':'entry_color', 'transform': predmap_mapper}) + + predmap_tooltips = [('index', '@{} | @{}'.format(row_name,col_name)), + ('density', '@{}'.format('density')), + ('similarity', '@{}'.format('similarity')), + ('uncertainty', '@{}'.format('uncertainty')), + ('prediction', '@{}'.format('prediction'))] + p3.add_tools( + HoverTool( + tooltips=predmap_tooltips, + mode='mouse', + renderers=[heatFig] + ) + ) + + # Upper mask + upperFig2 = p3.rect(x=col_name, y=row_name, source=upper_source, height=1, width=1, color="white") + upperFig2.visible = False + + with open('exploreML/models/active_explore_js/radio_call.js','r') as f: + radio_call_js = f.read() + + radio_call2 = CustomJS(args=dict(methods=clust_methods,clust_dict=clust_dict,plot=p3, + up_source=upper_source, up_dict=upper_dict),code=radio_call_js) + + radio_button_group.js_on_click(radio_call2) + + toggle.js_on_click(CustomJS(args=dict(plot=upperFig2),code=""" + console.log('toggle: active=' + this.active, this.toString()) + + plot.visible = cb_obj.active; + + """)) + + def update_predmap_button(attrname, old, new): + + # Obtain sampling type and respective slider value + curr_map_dict = slider_key_dict[radio_group_slider.active] + slider_val = str(sample_sliders[curr_map_dict].value) + + try: + curr_pred_map = predmap_key_dict[new] + print(curr_map_dict,curr_pred_map) + predmap_ds = map_dict[curr_map_dict][curr_pred_map] + except KeyError: + # If prediction type not in current sampling type, reset radio button + print('exception') + curr_pred_map = predmap_init_field + print(curr_map_dict,curr_pred_map) + predmap_ds = map_dict[curr_map_dict][curr_pred_map] + radio_button_predmap.active = predmap_labels_dict[predmap_init_field] + + # Update data sources and properties + # Update colorbar + predmap_color_bar.color_mapper.update(low=map_bounds.data[curr_pred_map][0], + palette=predmap_palette_dict[curr_pred_map], + high=map_bounds.data[curr_pred_map][1]) + + entry_values = predmap_ds.loc[:,slider_val].values if curr_pred_map not in constant_map else predmap_ds.loc[:,'0'].values + pred_ds.data['entry_color'] = entry_values + + p2.title.text, p3.title.text = '{} Manifold'.format(str.capitalize(curr_pred_map)),\ + '{} Heatmap'.format(str.capitalize(curr_pred_map)) + + radio_button_predmap = RadioButtonGroup(labels=list(map(str.capitalize,predmap_labels)), + active=predmap_labels_dict[predmap_init_field],max_width=300) + predmap_button_updates = radio_button_predmap.on_change('active', update_predmap_button) + + def update_slider_radio(attrname, old, new): + + # Obtain sampling type and respective slider value + curr_map_dict = slider_key_dict[new] + slider_val = str(sample_sliders[curr_map_dict].value) + + try: + curr_pred_map = predmap_key_dict[radio_button_predmap.active] + predmap_ds = map_dict[curr_map_dict][curr_pred_map] + + except KeyError: + # If prediction type not in current sampling type, reset radio button + curr_pred_map = predmap_init_field + predmap_ds = map_dict[curr_map_dict][curr_pred_map] + radio_button_predmap.active = predmap_labels_dict[predmap_init_field] + + # Update data sources and properties + # Update pred_df cds + entry_values = predmap_ds.loc[:,slider_val].values if curr_pred_map not in constant_map else predmap_ds.loc[:,'0'].values + pred_ds.data = pred_df.assign(entry_color=entry_values)\ + .assign(**{k:v.loc[:,slider_val].values if k not in constant_map else v.loc[:,'0'].values for k,v in map_dict[curr_map_dict].items()}) + + ########### Update map bounds for slider value + map_bounds.data = {k:[np.percentile(v.loc[:,slider_val].values if k not in constant_map else v.loc[:,'0'].values,map_q_low), + np.percentile(v.loc[:,slider_val].values if k not in constant_map else v.loc[:,'0'].values,map_q_high)]\ + for k,v in map_dict[curr_map_dict].items()} + map_bounds.data['prediction'] = [-0.3,0.3] + + # Update colorbar + predmap_color_bar.color_mapper.update(low=map_bounds.data[curr_pred_map][0], + palette=predmap_palette_dict[curr_pred_map], + high=map_bounds.data[curr_pred_map][1]) + ########### + + div1 = Div(text=""" + + """) + + radio_group_slider = RadioGroup(labels=[""]*len(sample_sliders.keys()), + active=slider_labels_dict[mapdictInit],name='slider_radio', + margin=(28,5,5,5), + css_classes=["spaced-radiogroup"], + width=15) + + radio_slider_updates = radio_group_slider.on_change('active', update_slider_radio) + + def update_predmap_slider(attrname, old, new): + + curr_map_dict = slider_key_dict[radio_group_slider.active] + curr_pred_map = predmap_key_dict[radio_button_predmap.active] + slider_val = str(sample_sliders[curr_map_dict].value) + + predmap_ds = map_dict[curr_map_dict][curr_pred_map] + + entry_values = predmap_ds.loc[:,slider_val].values if curr_pred_map not in constant_map else predmap_ds.loc[:,'0'].values + pred_ds.data = pred_df.assign(entry_color=entry_values)\ + .assign(**{k:v.loc[:,slider_val].values if k not in constant_map else v.loc[:,'0'].values for k,v in map_dict[curr_map_dict].items()}) + + ########### Update map bounds for slider value + map_bounds.data = {k:[np.percentile(v.loc[:,slider_val].values if k not in constant_map else v.loc[:,'0'].values,map_q_low), + np.percentile(v.loc[:,slider_val].values if k not in constant_map else v.loc[:,'0'].values,map_q_high)]\ + for k,v in map_dict[curr_map_dict].items()} + map_bounds.data['prediction'] = [-0.3,0.3] + + # Update colorbar + predmap_color_bar.color_mapper.update(low=map_bounds.data[curr_pred_map][0], + palette=predmap_palette_dict[curr_pred_map], + high=map_bounds.data[curr_pred_map][1]) + ########### + + slider_updates = {sampler:sample_sliders[sampler].on_change('value_throttled', update_predmap_slider)\ + for sampler in sample_sliders.keys()} + + def toggle_predmap_slider(event): + + if event: + update_predmap_slider('value_throttled',0,active_dim) + else: + update_predmap_slider('value_throttled',active_dim,0) + + data_toggle.on_click(toggle_predmap_slider) + + # with open('exploreML/models/active_explore_js/data_toggle.js','r') as f: + # data_toggle_js = f.read() + # + # data_toggle.js_on_click(CustomJS(args=dict(sliders=sample_sliders, active_dim=\ + # 1000), + # code=data_toggle_js)) + + #### Manifold plot + TOOLS = "save, pan, box_zoom, wheel_zoom, reset" + p2 = Figure(title='Prediction Manifold',tools=TOOLS,toolbar_location='above', + frame_width=plot_size, frame_height=plot_size, output_backend="webgl") + p2.xaxis.axis_label = "umap 1" + p2.yaxis.axis_label = "umap 2" + + manifoldFig = p2.scatter(x='x',y='y',source=pred_ds,size=1, + color={'field':'entry_color', 'transform': predmap_mapper}) + + p2.add_tools( + HoverTool( + tooltips=predmap_tooltips, + mode='mouse', + renderers=[manifoldFig] + ) + ) + + toolbar.children.append(radio_button_predmap) + toolbar.children[2] = row(radio_group_slider,toolbar.children[2]) + for child in toolbar.select_one({'name':'slider_col'}).children: + child.width = 275 + layout = column(p3,p2,div1) + + self.layout = layout + + def _addSym(self, df, change_col=True, ignore_index=True): + + if change_col: + symDF = pd.concat([df,df.assign(gene1=df['gene2'], + gene2=df['gene1'], + row_coord=df['col_coord'], + col_coord=df['row_coord'])],axis=0,ignore_index=ignore_index) + else: + symDF = pd.concat([df,df],ignore_index=ignore_index) + + return symDF