def test_can_add_multiple_glyph_renderers_to_legend_item(): legend_item = LegendItem() gr_1 = GlyphRenderer() gr_2 = GlyphRenderer() legend_item.renderers = [gr_1, gr_2] with mock.patch('bokeh.core.validation.check.logger') as mock_logger: check_integrity([legend_item]) assert mock_logger.error.call_count == 0
def test_legend_item_with_field_label_raises_error_if_field_not_in_cds(): legend_item = LegendItem() gr_1 = GlyphRenderer(data_source=ColumnDataSource()) legend_item.label = field('label') legend_item.renderers = [gr_1] with mock.patch('bokeh.core.validation.check.logger') as mock_logger: check_integrity([legend_item]) assert mock_logger.error.call_count == 1
def test_legend_item_with_value_label_and_different_data_sources_does_not_raise_a_validation_error(): legend_item = LegendItem() gr_1 = GlyphRenderer(data_source=ColumnDataSource()) gr_2 = GlyphRenderer(data_source=ColumnDataSource()) legend_item.label = value('label') legend_item.renderers = [gr_1, gr_2] with mock.patch('bokeh.core.validation.check.logger') as mock_logger: check_integrity([legend_item]) assert mock_logger.error.call_count == 0
def cleanLabels(self, nLabels=_MAX_NUMBER_OF_LINES): """ The function cleans all unused labels from the legend. nLabels specifies how many plots shall remain in the graph. :param nLabels: int :return: """ for i in range(nLabels, VibroP_GraphObject._MAX_NUMBER_OF_LINES): self.Graph.legend[0].items[i] = LegendItem(label="") self.Lines[i].glyph.line_color = 'white'
def defineLine(self, ID, Name, Color, Style): """ The function defines properties of a line. The user has to specify the line ID the class contains in order to change properties of a particular line. It is important to keep the order i.e. you have to start from the line with ID = 0, and iterate with the step 1. :param ID: int :param Name: string :param Color: string (according the bokeh documentation) :param Style: string (according the bokeh documentation) :return: """ self.Lines[ID].glyph.line_color = Color self.Lines[ID].glyph.line_dash = Style self.Graph.legend[0].items[ID] = LegendItem( label=Name, renderers=[ self.Lines[ID] ]) #self.Graph.legend[ 0 ].items[ ID ].label[ 'value' ] = Name
def codon(enrichments=None, group_by='codon', groupings=None, colors=None, y_max=5, x_lims=(-25, 25), unselected_alpha=0.2, initial_menu_selection=None, initial_top_group_selections=None, initial_sub_group_selections=None, intial_resolution='codon', ): ''' An interactive plot of metacodon enrichment profiles using bokeh. Call without any arguments for an example using data from Jan et al. Science 2014. Args: enrichments: A multi-level dictionary of enrichment values to plot, laid out like: enrichments = { 'codon': { 'xs': [list of codon offset values], 'experiment_1': { 'TTT': [list of codon-resolution enrichments], 'TTC': ..., ..., }, 'experiment_2': {...}, }, 'nucleotide': { 'xs': [list of nucleotide offset values], 'experiment_1': { 'TTT': [list of nucleotide-resolution enrichments], 'TTC': ..., ..., }, 'experiment_2': {...}, }, } See example_metacodon_enrichments.json in the same directory as this file for an example. (If enrichments == None, loads this file, which contains processed data from Jan et al. Science 2014, as an example. Other arguments are overwritten to highlight interesting features.) group_by: If 'codon', plot enrichments around one codon for all experiments. If 'experiment', plot enrichments around all codons for one experiment. groupings: If not None, a dictionary of groups of experiments/codons to list together for convenient selection. If None and group_by == 'experiment', experiment names will be grouped by name.split(':')[0]. If None and group_by == 'codon', codons will be grouped by amino acid. ''' if initial_top_group_selections is None: initial_top_group_selections = [] if initial_sub_group_selections is None: initial_sub_group_selections = [] if enrichments is None: # Load example data dirname = os.path.dirname(__file__) fn = os.path.join(dirname, 'example_metacodon_enrichments.json') with open(fn) as fh: enrichments = json.load(fh) # Prepare example groupings. exp_names = set(enrichments['codon'].keys()) exp_names.remove('xs') group_names = ['-CHX', '+CHX_2min', '+CHX_7min'] groupings = {} for group_name in group_names: groupings[group_name] = [n for n in exp_names if group_name in n] # Overrule other arguments to highlight interesting features in data. x_lims = (-18, 81) y_max = 3.2 group_by = 'experiment' initial_menu_selection = 'CGA' initial_sub_group_selections = [ 'BirA_+CHX_2minBiotin_input', 'BirAmVenusUbc6_+CHX_7minBiotin_input', 'sec63mVenusBirA_-CHX_1minBiotin_input', ] # enrichments has experiments as top level keys and codons as lower level # keys. Building ColumnDataSource's below assumes a dictionary with # checkbox_names as the top keys, so if grouping by experiment, don't need # to do anything. If grouping by codon, need to invert the order of the # dictionary. # Copy enrichments since xs will be popped. enrichments = copy.deepcopy(enrichments) xs = { 'codon': enrichments['codon'].pop('xs'), 'nucleotide': enrichments['nucleotide'].pop('xs'), } exp_names = sorted(enrichments['codon']) if group_by == 'codon': if groupings is None: groupings = {aa: cs for aa, cs in genetic_code.full_back_table.items() if aa != '*'} menu_options = exp_names checkbox_names = genetic_code.non_stop_codons inverted = {} for resolution in enrichments: inverted[resolution] = {} for checkbox_name in checkbox_names: inverted[resolution][checkbox_name] = {} for menu_name in menu_options: inverted[resolution][checkbox_name][menu_name] = enrichments[resolution][menu_name][checkbox_name] enrichments = inverted elif group_by == 'experiment': menu_options = genetic_code.non_stop_codons checkbox_names = sorted(enrichments['codon']) if groupings is None: groupings = defaultdict(list) for checkbox_name in sorted(checkbox_names): group_name = checkbox_name.split(':')[0] groupings[group_name].append(checkbox_name) if colors is None: colors = dict(zip(checkbox_names, cycle(colors_list))) if initial_menu_selection is None: initial_menu_selection = menu_options[0] if initial_menu_selection not in menu_options: raise ValueError('{0} not in {1}'.format(initial_menu_selection, menu_options)) # Select all members of any initially selected top_group. initial_sub_group_selections = set(initial_sub_group_selections) for top_name, sub_names in sorted(groupings.items()): if top_name in initial_top_group_selections: initial_sub_group_selections.update(sub_names) # Build ColumnDataSource's from enrichments. sources = {} for key in ['plotted', 'codon', 'nucleotide']: if key == 'plotted': resolution = intial_resolution else: resolution = key sources[key] = {} for checkbox_name in sorted(enrichments[resolution]): source = bokeh.models.ColumnDataSource(enrichments[resolution][checkbox_name]) source.data['x'] = xs[resolution] source.data['y'] = source.data[initial_menu_selection] source.data['name'] = [checkbox_name] * len(xs[resolution]) source.name = 'source_{0}_{1}'.format(checkbox_name, key) sources[key][checkbox_name] = source # Set up the actual plot. tools = [ 'pan', 'tap', 'box_zoom', 'wheel_zoom', 'save', 'reset', 'undo', ] fig = bokeh.plotting.figure(plot_width=1200, plot_height=800, tools=tools, active_scroll='wheel_zoom', name='figure', ) fig.toolbar.logo = None fig.grid.grid_line_alpha = 0.4 fig.y_range = bokeh.models.Range1d(0, y_max) fig.y_range.name = 'y_range' fig.x_range = bokeh.models.Range1d(*x_lims) fig.x_range.name = 'x_range' range_callback = build_callback('metacodon_range') fig.y_range.callback = range_callback fig.x_range.callback = range_callback fig.xaxis.axis_label = 'Offset ({0}s)'.format(intial_resolution) fig.yaxis.axis_label = 'Mean relative enrichment' fig.xaxis.name = 'x_axis' fig.yaxis.name = 'y_axis' fig.xaxis.axis_label_text_font_style = 'normal' fig.yaxis.axis_label_text_font_style = 'normal' fig.xaxis[0].ticker = bokeh.models.tickers.SingleIntervalTicker(interval=3, num_minor_ticks=3) legend_items = [] initial_legend_items = [] lines = [] for checkbox_name, source in sources['plotted'].items(): if checkbox_name in initial_sub_group_selections: color = colors[checkbox_name] line_width = 2 line_alpha = 0.95 circle_visible = True else: color ='black' line_width = 1 circle_visible = False if len(initial_sub_group_selections) > 0: line_alpha = unselected_alpha else: line_alpha = 0.6 line = fig.line(x='x', y='y', color=color, source=source, line_width=line_width, line_alpha=line_alpha, line_join='round', nonselection_line_color=colors[checkbox_name], nonselection_line_alpha=unselected_alpha, hover_alpha=1.0, hover_color=colors[checkbox_name], ) line.hover_glyph.line_width = 3 line.name = 'line_{0}'.format(checkbox_name) lines.append(line) circle = fig.circle(x='x', y='y', color=colors[checkbox_name], source=source, size=2.5, fill_alpha=0.95, line_alpha=0.95, visible=circle_visible, hover_alpha=0.95, hover_color=colors[checkbox_name], ) circle.hover_glyph.visible = True circle.name = 'circle_{0}'.format(checkbox_name) legend_item = LegendItem(label=checkbox_name, renderers=[line]) legend_items.append(legend_item) if checkbox_name in initial_sub_group_selections: initial_legend_items.append(legend_item) legend = ToggleLegend(name='legend', items=initial_legend_items, all_items=legend_items, ) fig.add_layout(legend) fig.legend.location = 'top_right' fig.legend.background_fill_alpha = 0.5 source_callback = build_callback('metacodon_selection') for source in sources['plotted'].values(): source.callback = source_callback hover = bokeh.models.HoverTool(line_policy='interp', renderers=lines, ) hover.tooltips = [('name', '@name')] fig.add_tools(hover) # Draw horizontal and vertical lines. zero_x = bokeh.models.annotations.Span(location=0, dimension='height', line_color='black', line_alpha=0.8, ) fig.renderers.append(zero_x) one_y = bokeh.models.annotations.Span(location=1, dimension='width', line_color='black', line_alpha=0.8, ) fig.renderers.append(one_y) menu_title = 'Codon:' if group_by == 'experiment' else 'Experiment:' menu = bokeh.models.widgets.MultiSelect(options=menu_options, value=[initial_menu_selection], size=min(30, len(menu_options)), title=menu_title, ) menu.callback = build_callback('metacodon_menu') sub_group_callback = build_callback('metacodon_sub_group', format_kwargs=dict(color_unselected='false'), ) top_group_callback = build_callback('metacodon_top_group') top_groups = [] sub_groups = [] for top_name, sub_names in sorted(groupings.items()): width = int(75 + max(len(l) for l in sub_names) * 6.5) top_active = [0] if top_name in initial_top_group_selections else [] top = bokeh.models.widgets.CheckboxGroup(labels=[top_name], active=top_active, width=width, name='top_{0}'.format(top_name), callback=top_group_callback, ) top_groups.append(top) sub_active = [i for i, n in enumerate(sub_names) if n in initial_sub_group_selections] sub = bokeh.models.widgets.CheckboxGroup(labels=sub_names, active=sub_active, width=width, callback=sub_group_callback, name='sub_{0}'.format(top_name), ) sub_groups.append(sub) highest_level_chooser = bokeh.models.widgets.RadioGroup(labels=['codon resolution', 'nucleotide resolution'], active=0 if intial_resolution == 'codon' else 1, name='highest_level_chooser', ) injection_sources = [] for resolution in ['codon', 'nucleotide']: injection_sources.extend(sources[resolution].values()) injection = {'ensure_no_collision_{0}'.format(i): v for i, v in enumerate(injection_sources)} highest_level_chooser.callback = build_callback('metacodon_resolution', args=injection, ) clear_selection = bokeh.models.widgets.Button(label='Clear selection') clear_selection.callback = build_callback('metacodon_clear_selection') alpha_slider = bokeh.models.Slider(start=0., end=1., value=unselected_alpha, step=.05, title='unselected alpha', ) alpha_slider.callback = build_callback('lengths_unselected_alpha') widgets = [ menu, highest_level_chooser, alpha_slider, clear_selection, ] grid = [ top_groups, sub_groups, [fig, bokeh.layouts.widgetbox(widgets)], ] bokeh.io.show(bokeh.layouts.layout(grid))
def _display_timeline_dict(data: dict, **kwargs) -> figure: # noqa: C901, MC0001 """ Display a timeline of events. Parameters ---------- data : dict Data points to plot on the timeline. Need to contain: Key - Name of data type to be displayed in legend Value - dict of data containing: data : pd.DataFrame Data to plot time_column : str Name of the timestamp column source_columns : list List of source columns to use in tooltips color: str Color of datapoints for this data Other Parameters ---------------- ref_time : datetime, optional Input reference line to display (the default is None) title : str, optional Title to display (the default is None) time_column : str, optional Name of the timestamp column (the default is 'TimeGenerated') legend: str, optional Where to position the legend None, left, right or inline (default is None) yaxis : bool, optional Whether to show the yaxis and labels range_tool : bool, optional Show the the range slider tool (default is True) source_columns : list, optional List of default source columns to use in tooltips (the default is None) height : int, optional The height of the plot figure (the default is auto-calculated height) width : int, optional The width of the plot figure (the default is 900) Returns ------- figure The bokeh plot figure. """ reset_output() output_notebook() height: int = kwargs.pop("height", None) width: int = kwargs.pop("width", 900) ref_time: Any = kwargs.pop("ref_time", None) ref_label: str = kwargs.pop("ref_label", None) title: str = kwargs.pop("title", None) legend_pos: str = kwargs.pop("legend", None) show_yaxis: bool = kwargs.pop("yaxis", False) show_range: bool = kwargs.pop("range_tool", True) xgrid: bool = kwargs.pop("xgrid", True) ygrid: bool = kwargs.pop("ygrid", False) hide: bool = kwargs.pop("hide", False) tool_tip_columns, min_time, max_time = _unpack_data_series_dict( data, **kwargs) series_count = len(data) tooltips, formatters = _create_tool_tips(data, tool_tip_columns) hover = HoverTool(tooltips=tooltips, formatters=formatters) title = f"Timeline: {title}" if title else "Event Timeline" try: start_range = min_time - ((max_time - min_time) * 0.1) end_range = max_time + ((max_time - min_time) * 0.1) except OutOfBoundsDatetime: min_time = min_time.to_pydatetime() max_time = max_time.to_pydatetime() start_range = min_time - ((max_time - min_time) * 0.1) end_range = max_time + ((max_time - min_time) * 0.1) height = height if height else _calc_auto_plot_height(len(data)) y_range = ((-1 / series_count), series_count - 1 + (1 / series_count)) plot = figure( x_range=(start_range, end_range), y_range=y_range, min_border_left=50, plot_height=height, plot_width=width, x_axis_label="Event Time", x_axis_type="datetime", x_minor_ticks=10, tools=[hover, "xwheel_zoom", "box_zoom", "reset", "save", "xpan"], title=title, ) plot.yaxis.visible = show_yaxis if show_yaxis: if data: y_labels = { ser_def["y_index"]: str(lbl) for lbl, ser_def in data.items() } plot.yaxis.major_label_overrides = y_labels if ygrid: plot.ygrid.minor_grid_line_color = "navy" plot.ygrid.minor_grid_line_alpha = 0.1 plot.ygrid.grid_line_color = "navy" plot.ygrid.grid_line_alpha = 0.3 else: plot.ygrid.grid_line_color = None if xgrid: plot.xgrid.minor_grid_line_color = "navy" plot.xgrid.minor_grid_line_alpha = 0.3 else: plot.xgrid.grid_line_color = None # Create plot bar to act as as range selector rng_select = _create_range_tool( data=data, min_time=min_time, max_time=max_time, plot_range=plot.x_range, width=width, height=height, ) # set the tick datetime formatter plot.xaxis[0].formatter = _get_tick_formatter() if series_count > 1 and not legend_pos: legend_pos = "left" # plot groups individually so that we can create an interactive legend # if legend_pos is "inline", we add add the normal legend inside the plot # if legend_pos is "left" or "right", we add the legend to the side legend_items = [] for ser_name, series_def in data.items(): if legend_pos == "inline": p_series = plot.diamond( x=series_def["time_column"], y="y_index", color=series_def["color"], alpha=0.5, size=10, source=series_def["source"], legend_label=str(ser_name), ) else: p_series = plot.diamond( x=series_def["time_column"], y="y_index", color=series_def["color"], alpha=0.5, size=10, source=series_def["source"], ) if legend_pos in ["left", "right"]: legend_items.append( LegendItem( label=str(ser_name), renderers=[p_series], )) if legend_pos == "inline": # Position the inline legend plot.legend.location = "center_left" plot.legend.click_policy = "hide" elif legend_pos in ["left", "right"]: # Create the legend box outside of the plot area ext_legend = Legend( items= legend_items[::-1], # the legend is in the wrong order otherwise location="center", click_policy="hide", label_text_font_size="8pt", ) plot.add_layout(ext_legend, legend_pos) if ref_time is not None: _add_ref_line(plot, ref_time, ref_label, len(data)) if show_range: plot_layout = column(plot, rng_select) else: plot_layout = plot if not hide: show(plot_layout) return plot_layout
def plotModesInBand(GraphInstance): """ At the beginning the function cleans the GraphInstance from the previous plot. Then it adjusts the following lines with corresponding labels and colors on the plot: ModesInBand - bending ModesInBand - shear ModesInBand - compressional :param GraphInstance: an instance of GraphObject class :return: """ GraphInstance.cleanGraph() GraphInstance.cleanLabels(3) GraphInstance.Graph.yaxis.axis_label = "Number of Modes per one-third octave band" GraphInstance.Graph.xaxis.axis_label = "Frequency in Hz" # ............................ bending_np graph ............................ # 'Effective bending (thick plate)' GraphInstance.GraphData[ 0 ].data \ = dict( XData = GraphInstance.Containers[ "ModesInBand" ][ "freq_T" ], YData = GraphInstance.Containers[ "ModesInBand" ][ "bending" ] ) GraphInstance.defineLine(0, 'Effective bending (thick plate)', GREEN, 'solid') GraphInstance.Circles[ 0 ].data_source.data.\ update({"x" : GraphInstance.GraphData[ 0 ].data[ "XData" ], "y" : GraphInstance.GraphData[ 0 ].data[ "YData" ]}) GraphInstance.Graph.legend[ 0 ].items[ 0 ] \ = LegendItem( label = 'Effective bending (thick plate)', renderers = [ GraphInstance.Lines[ 0 ], GraphInstance.Circles[ 0 ] ] ) GraphInstance.Circles[0].glyph.line_color = GREEN GraphInstance.Circles[0].glyph.fill_color = GREEN # ............................ compressional_np graph ...................... # 'Shear, in-plane' GraphInstance.GraphData[ 1 ].data \ = dict( XData = GraphInstance.Containers[ "ModesInBand" ][ "freq_T" ], YData = GraphInstance.Containers[ "ModesInBand" ][ "shear" ] ) GraphInstance.defineLine(1, 'Shear, in-plane', LIGHT_BLUE, 'solid') GraphInstance.Circles[ 1 ].data_source.\ data.update({"x" : GraphInstance.GraphData[ 1 ].data[ "XData" ], "y" : GraphInstance.GraphData[ 1 ].data[ "YData" ]}) GraphInstance.Graph.legend[ 0 ].items[ 1 ] \ = LegendItem( label = 'Shear, in-plane', renderers = [ GraphInstance.Lines[ 1 ], GraphInstance.Circles[ 1 ] ] ) GraphInstance.Circles[1].glyph.line_color = LIGHT_BLUE GraphInstance.Circles[1].glyph.fill_color = LIGHT_BLUE # ............................ shear_np graph .............................. # 'Quasi-longitudial, in plane' GraphInstance.GraphData[ 2 ].data \ = dict( XData = GraphInstance.Containers[ "ModesInBand" ][ "freq_T" ], YData = GraphInstance.Containers[ "ModesInBand" ][ "compressional" ] ) GraphInstance.defineLine(2, 'Quasi-longitudinal, in plane', DARK_BLUE, 'solid') GraphInstance.Circles[ 2 ].data_source.data.\ update({"x" : GraphInstance.GraphData[ 2 ].data[ "XData" ], "y" : GraphInstance.GraphData[ 2 ].data[ "YData" ]}) GraphInstance.Graph.legend[ 0 ].items[ 2 ] \ = LegendItem( label = 'Quasi-longitudial, in plane', renderers = [ GraphInstance.Lines[ 2 ], GraphInstance.Circles[ 2 ] ] ) GraphInstance.Circles[2].glyph.line_color = DARK_BLUE GraphInstance.Circles[2].glyph.fill_color = DARK_BLUE
def gene(enrichments=None, groupings=None, y_max=5, unselected_alpha=0.2, initial_resolution='nucleotide', initial_top_group_selections=None, initial_sub_group_selections=None, ): ''' An interactive plot of metagene enrichment profiles using bokeh. Call without any arguments for an example using data from Jan et al. Science 2014. Args: enrichments: A multi-level dictionary of enrichment values to plot, laid out like: enrichments = { 'codon': { 'start_codon': { 'xs': [list of codon offset values relative to start codon], 'experiment_1': [list of enrichment values], 'experiment_2': [...], ..., }, 'stop_codon': {...} }, 'nucleotide': {...} } See example_metagene_enrichments.json in the same directory as this file for an example. (If enrichments == None, loads this file, which contains processed data from Jan et al. Science 2014, as an example.) groupings: If not None, a dictionary of groups of experiments to list together for convenient selection. If None, experiment names will be grouped by name.split(':')[0]. ''' if initial_top_group_selections is None: initial_top_group_selections = [] if initial_sub_group_selections is None: initial_sub_group_selections = [] if enrichments is None: # Load example data dirname = os.path.dirname(__file__) fn = os.path.join(dirname, 'example_metagene_enrichments.json') with open(fn) as fh: enrichments = json.load(fh) # Prepare example groupings. exp_names = set(enrichments['codon']['start_codon'].keys()) exp_names.remove('xs') group_names = ['-CHX', '+CHX_2min', '+CHX_7min'] groupings = {} for group_name in group_names: groupings[group_name] = [n for n in exp_names if group_name in n] # Overrule other arguments to highlight interesting features in data. initial_resolution = 'codon' y_max = 18 initial_sub_group_selections = [ 'BirA_+CHX_2minBiotin_input', 'BirAmVenusUbc6_+CHX_7minBiotin_input', 'sec63mVenusBirA_-CHX_1minBiotin_input', ] initial_sub_group_selections = set(initial_sub_group_selections) for top_name, sub_names in sorted(groupings.items()): if top_name in initial_top_group_selections: initial_sub_group_selections.update(sub_names) enrichments = copy.deepcopy(enrichments) highest_level_keys = sorted(enrichments) all_xs = {} for key in highest_level_keys: all_xs[key] = {} for landmark in enrichments[key]: all_xs[key][landmark] = enrichments[key][landmark].pop('xs') exp_names = sorted(enrichments[highest_level_keys[0]]['start_codon']) sources = {} for key in ['plotted'] + highest_level_keys: if key == 'plotted': resolution = initial_resolution else: resolution = key sources[key] = {} for exp_name in exp_names: source = bokeh.models.ColumnDataSource() for landmark in ['start_codon', 'stop_codon']: xs = all_xs[resolution][landmark] ys = enrichments[resolution][landmark][exp_name] source.data['xs_{0}'.format(landmark)] = xs source.data['ys_{0}'.format(landmark)] = ys source.data['name'] = [exp_name] * len(xs) source.name = 'source_{0}_{1}'.format(exp_name, key) sources[key][exp_name] = source initial_before = 20 initial_after = 150 initial_lims = { 'start_codon': (-initial_before, initial_after), 'stop_codon': (-initial_after, initial_before), } x_ranges = {} x_range_callback = build_callback('metagene_x_range') for landmark in ('start_codon', 'stop_codon'): x_range = bokeh.models.Range1d(*initial_lims[landmark]) x_range.name = 'x_range_{0}'.format(landmark) x_range.callback = x_range_callback x_ranges[landmark] = x_range y_range = bokeh.models.Range1d(0, y_max) y_range.name = 'y_range' y_range.callback = build_callback('metagene_y_range') tools = [ 'pan', 'tap', 'box_zoom', 'wheel_zoom', 'save', 'reset', 'undo', ] figs = {} for key, y_axis_location in [('start_codon', 'left'), ('stop_codon', 'right')]: figs[key] = bokeh.plotting.figure(plot_width=800, plot_height=600, x_range=x_ranges[key], y_range=y_range, y_axis_location=y_axis_location, tools=tools, active_scroll='wheel_zoom', ) lines = { 'start_codon': [], 'stop_codon': [], } legend_items = [] initial_legend_items = [] colors = dict(zip(exp_names, cycle(colors_list))) for exp_name in exp_names: if exp_name in initial_sub_group_selections: color = colors[exp_name] line_width = 2 line_alpha = 0.95 circle_visible = True else: color = 'black' line_width = 1 circle_visible = False if len(initial_sub_group_selections) > 0: line_alpha = unselected_alpha else: line_alpha = 0.6 for landmark in ('start_codon', 'stop_codon'): line = figs[landmark].line(x='xs_{0}'.format(landmark), y='ys_{0}'.format(landmark), source=sources['plotted'][exp_name], color=color, nonselection_line_color=colors[exp_name], nonselection_line_alpha=unselected_alpha, hover_alpha=1.0, hover_color=colors[exp_name], line_width=line_width, line_alpha=line_alpha, ) line.hover_glyph.line_width = 4 line.name = 'line_{0}'.format(exp_name) lines[landmark].append(line) circle = figs[landmark].circle(x='xs_{0}'.format(landmark), y='ys_{0}'.format(landmark), source=sources['plotted'][exp_name], size=2, color=colors[exp_name], fill_alpha=0.9, line_alpha=0.9, visible=circle_visible, hover_alpha=1.0, hover_color=colors[exp_name], ) circle.hover_glyph.visible = True circle.name = 'circle_{0}'.format(exp_name) if landmark == 'stop_codon': legend_item = LegendItem(label=exp_name, renderers=[line]) legend_items.append(legend_item) if exp_name in initial_sub_group_selections: initial_legend_items.append(legend_item) legend = ToggleLegend(name='legend', items=initial_legend_items, all_items=legend_items, ) figs['stop_codon'].add_layout(legend) figs['stop_codon'].legend.location = 'top_left' figs['stop_codon'].legend.background_fill_alpha = 0.5 for landmark, fig in figs.items(): zero_x = bokeh.models.annotations.Span(location=0, dimension='height', line_color='black', line_alpha=0.5, ) fig.renderers.append(zero_x) one_y = bokeh.models.annotations.Span(location=1, dimension='width', line_color='black', line_alpha=0.5, ) fig.renderers.append(one_y) remove_underscore = ' '.join(landmark.split('_')) fig.xaxis.axis_label = 'Offset from {0}'.format(remove_underscore) fig.xaxis.axis_label_text_font_style = 'normal' fig.grid.grid_line_alpha = 0.4 figs['start_codon'].yaxis.axis_label = 'Mean relative enrichment' figs['start_codon'].yaxis.axis_label_text_font_style = 'normal' source_callback = build_callback('metacodon_selection') for source in sources['plotted'].values(): source.callback = source_callback for landmark in ['start_codon', 'stop_codon']: hover = bokeh.models.HoverTool(line_policy='interp', renderers=lines[landmark], ) hover.tooltips = [('name', '@name')] figs[landmark].add_tools(hover) resolution = bokeh.models.widgets.RadioGroup(labels=['codon resolution', 'nucleotide resolution'], active=0 if initial_resolution == 'codon' else 1, ) resolution.name = 'resolution' injection_sources = [] for key in highest_level_keys: injection_sources.extend(sources[key].values()) injection = {'ensure_no_collision_{0}'.format(i): v for i, v in enumerate(injection_sources)} resolution.callback = build_callback('metacodon_resolution', args=injection, ) sub_group_callback = build_callback('metacodon_sub_group', format_kwargs=dict(color_unselected='false'), ) top_group_callback = build_callback('metacodon_top_group') top_groups = [] sub_groups = [] for top_name, sub_names in sorted(groupings.items()): width = int(75 + max(len(l) for l in sub_names) * 6.5) top_active = [0] if top_name in initial_top_group_selections else [] top = bokeh.models.widgets.CheckboxGroup(labels=[top_name], active=top_active, width=width, name='top_{0}'.format(top_name), callback=top_group_callback, ) top_groups.append(top) sub_active = [i for i, n in enumerate(sub_names) if n in initial_sub_group_selections] sub = bokeh.models.widgets.CheckboxGroup(labels=sorted(sub_names), active=sub_active, width=width, callback=sub_group_callback, name='sub_{0}'.format(top_name), ) sub_groups.append(sub) alpha_slider = bokeh.models.Slider(start=0., end=1., value=unselected_alpha, step=.05, title='unselected alpha', ) alpha_slider.callback = build_callback('lengths_unselected_alpha') plots = bokeh.layouts.gridplot([[figs['start_codon'], figs['stop_codon']]]) plots.children[0].logo = None grid = [ top_groups, sub_groups, [plots, bokeh.layouts.widgetbox([resolution, alpha_slider])], ] bokeh.io.show(bokeh.layouts.layout(grid))
def create_plot_figure(self, active_tab): """ create a new plot and insert it in given tab. """ #find table name of active tab and its bokeh instances test = active_tab.name#contains csv filename x_sel=active_tab.select_one({'name':'x_sel'}) y_sel=active_tab.select_one({'name':'y_sel'}) y_sel2=active_tab.select_one({'name':'y_sel2'}) plot_df = self.plot_dfs[test] source = ColumnDataSource(plot_df) #Replace entirely p with a new plot p = Plot( x_range=DataRange1d(), y_range=DataRange1d(), plot_height=600, plot_width=600, title=Title(text=self.sel_csv), name='plot') p.add_tools(BoxZoomTool(), SaveTool(), ResetTool(), PanTool(), HoverTool(tooltips=[('x','$x'), ('y','$y')])) #see https://bokeh.github.io/blog/2017/7/5/idiomatic_bokeh/ x_axis = LinearAxis( axis_label = x_sel.value, ticker=BasicTicker(desired_num_ticks =10), name='x_axis') y_axis = LinearAxis( axis_label = y_sel.value, ticker=BasicTicker(desired_num_ticks =10), name='y_axis') #primary y-axis ly = p.add_glyph(source, Line(x=x_sel.value, y=y_sel.value, line_width=2, line_color='black'), name = 'ly' ) p.add_layout(x_axis,'below') p.add_layout(y_axis,'left') p.y_range.renderers = [ly] #secondary y-axis if y_sel2.value.strip() != 'None':#secondary y-axis y_axis2 = LinearAxis( axis_label = y_sel2.value, ticker=BasicTicker(desired_num_ticks=10), name='y_axis2', y_range_name='right_axis') p.add_layout(y_axis2,'right') p.extra_y_ranges = {"right_axis": DataRange1d()} ly2 = p.add_glyph(source, Line(x=x_sel.value, y=y_sel2.value, line_width=2, line_color='red'), y_range_name='right_axis', name = 'ly2' ) p.extra_y_ranges['right_axis'].renderers = [ly2] leg_items = [LegendItem(label=y_sel.value, renderers=[ly]), LegendItem(label=y_sel2.value, renderers=[ly2])] else: leg_items = [LegendItem(label=y_sel.value, renderers=[ly])] p.add_layout(Legend(items=leg_items, location='top_right') ) active_tab.child.children[1] = p return p
def plot(exps, initial_sub_group_selections=None, unselected_alpha=0.2, ): if initial_sub_group_selections is None: initial_sub_group_selections = [] groupings = {group_name: sorted(exps['raw'][group_name]) for group_name in exps['raw']} sources = {} colors = {} color_iter = cycle(colors_list) highest_level_keys = [k for k in sorted(exps) if k != 'descriptions'] for key in ['plotted'] + highest_level_keys: if key == 'plotted': normalization = 'raw' else: normalization = key sources[key] = {} for group_name in sorted(exps[normalization]): for sample_name in sorted(exps[normalization][group_name]): if sample_name not in colors: colors[sample_name] = color_iter.next() series = exps[normalization][group_name][sample_name] source = bokeh.models.ColumnDataSource() source.data['x'] = list(series.index) source.data['y'] = list(series) source.data['name'] = [sample_name] * len(series) source.name = 'source_{0}_{1}'.format(sample_name, key) sources[key][sample_name] = source sample_names = [] descriptions = [] for group_name, group in sorted(exps['descriptions'].items()): for sample_name, description in sorted(group.items()): sample_names.append(sample_name) descriptions.append(description) full_source = bokeh.models.ColumnDataSource(name='full_source') full_source.data = { 'sample_name': sample_names, 'description': descriptions, } tools = [ 'pan', 'tap', 'box_zoom', 'wheel_zoom', 'save', 'reset', 'undo', ] x_range = bokeh.models.Range1d(17, 70, bounds=(17, 70)) fig = bokeh.plotting.figure(plot_width=1200, plot_height=600, tools=tools, active_scroll='wheel_zoom', x_range=x_range, ) random_group = exps['raw'].keys()[0] series = exps['raw'][random_group]['{0}:12_Ladder'.format(random_group)] peak_times = extract_ladder_peak_times(series) for time, nt in peak_times.items(): line = bokeh.models.annotations.Span(location=time, dimension='height', line_color='black', line_alpha=0.8, line_dash='dashed', ) fig.renderers.append(line) fig.xgrid.grid_line_color = None fig.ygrid.grid_line_color = None convert_tick = ''' peak_times = {dict}; return peak_times[tick]; '''.format(dict=peak_times) fig.xaxis.formatter = bokeh.models.FuncTickFormatter(code=convert_tick) fig.xaxis.ticker = bokeh.models.FixedTicker(ticks=list(peak_times)) all_legend_items = [] initial_legend_items = [] lines = [] for sample_name, source in sources['plotted'].items(): if sample_name in initial_sub_group_selections: color = colors[sample_name] line_width = 2 line_alpha = 0.95 else: color = 'black' line_width = 1 if len(initial_sub_group_selections) > 0: line_alpha = unselected_alpha else: line_alpha = 0.6 line = fig.line(x='x', y='y', color=color, source=source, line_width=line_width, line_alpha=line_alpha, line_join='round', nonselection_line_color=colors[sample_name], nonselection_line_alpha=unselected_alpha, hover_alpha=1.0, hover_color=colors[sample_name], ) line.hover_glyph.line_width = 4 line.name = 'line_{0}'.format(sample_name) lines.append(line) legend_item = LegendItem(label=sample_name, renderers=[line]) all_legend_items.append(legend_item) if sample_name in initial_sub_group_selections: initial_legend_items.append(legend_item) legend = ToggleLegend(name='legend', items=initial_legend_items, all_items=all_legend_items, ) fig.add_layout(legend) source_callback = external_coffeescript('bioanalyzer_selection', args=dict(full_source=full_source), ) for source in sources['plotted'].values(): source.callback = source_callback hover = bokeh.models.HoverTool(line_policy='interp', renderers=lines, ) hover.tooltips = [('name', '@name')] fig.add_tools(hover) sub_group_callback = external_coffeescript('bioanalyzer_sub_group', args=dict(full_source=full_source), ) top_group_callback = external_coffeescript('bioanalyzer_top_group', args=dict(full_source=full_source), ) top_groups = [] sub_groups = [] for top_name, sub_names in sorted(groupings.items()): width = 75 + max(len(l) for l in sub_names) * 6 top = bokeh.models.widgets.CheckboxGroup(labels=[top_name], active=[], width=width, name='top_{0}'.format(top_name), callback=top_group_callback, ) top_groups.append(top) sub_active = [i for i, n in enumerate(sub_names) if n in initial_sub_group_selections] sub = bokeh.models.widgets.CheckboxGroup(labels=sub_names, active=sub_active, width=width, callback=sub_group_callback, name='sub_{0}'.format(top_name), ) sub_groups.append(sub) clear_selection = bokeh.models.widgets.Button(label='Clear selection') clear_selection.callback = external_coffeescript('bioanalyzer_clear_selection', args=dict(full_source=full_source), ) highest_level_chooser = bokeh.models.widgets.Select(options=highest_level_keys, value=highest_level_keys[2], ) callback_name = 'metacodon_highest_level' injection_sources = [] for key in highest_level_keys: injection_sources.extend(sources[key].values()) injection = {'ensure_no_collision_{0}'.format(i): v for i, v in enumerate(injection_sources)} highest_level_chooser.callback = external_coffeescript(callback_name, args=injection, ) table_col_names = [ ('sample_name', 250), ('description', 850), ] columns = [] for col_name, width in table_col_names: column = bokeh.models.widgets.TableColumn(field=col_name, title=col_name, formatter=None, width=width, ) columns.append(column) filtered_data = {k: [] for k, _ in table_col_names} filtered_source = bokeh.models.ColumnDataSource(data=filtered_data, name='table_source') table = bokeh.models.widgets.DataTable(source=filtered_source, columns=columns, width=1200, height=400, sortable=False, name='table', row_headers=False, ) grid = bokeh.layouts.layout([ sub_groups + [bokeh.layouts.widgetbox([highest_level_chooser, clear_selection])], [fig], [table], ]) bokeh.io.show(grid)
def add_path(self, source_label, links=None, edge_type='curved', tooltips=None, legend=None, **kwargs): """ Connect all points in the datasource in a path (to show order). `self.prepare_plot` must have been called previous to this. Parameters: source_label (str): string corresponding to a label previously called in `self.add_source` order_col: the column of a data source specifying the order of the records tooltips: string or list of tuples (passed to Bokeh HoverTool) legend (str): name to assign to this layer in the plot legend kwargs: options passed to the objected for `bokeh_model` This method allows two special kwargs: 'color' and 'alpha'. When used with a bokeh model that has 'fill_color' and 'line_color' and 'fill_alpha' and 'line_alpha' properties, calling the special kwarg will use the same value for both. """ self._validate_workflow('add_path') source = self.sources[source_label].copy() suffix = '_transform' if type(self.plot) != GMapPlot else '' x_point_label = 'x_coord_point{}'.format(suffix) y_point_label = 'y_coord_point{}'.format(suffix) if all(isinstance(x, (int, float)) for x in source[links].tolist()): x1 = source.sort_values(links)[x_point_label].values y1 = source.sort_values(links)[y_point_label].values x2 = x1[1:] y2 = y1[1:] x1 = x1[:-1] y1 = y1[:-1] x3 = (x1 + x2) / 2 y3 = (y1 + y2) / 2 xc = x3 + abs(y3 - y2) yc = y3 + abs(x3 - x2) new_source = { 'x1': x1, 'x2': x2, 'xc': xc, 'y1': y1, 'y2': y2, 'yc': yc } for c in source.columns: if (c not in self.omit_columns) and c not in new_source: new_source[c] = source[c].values[:-1] elif all( isinstance(x, (list, tuple, set)) for x in source[links].tolist()): if 'uid' not in source.columns: raise ValueError( 'Source must contain column `uid` when links is a list of iterables.' ) nodes = source['uid'].tolist() edges = source[links].tolist() node_x = source.set_index('uid')[x_point_label].to_dict() node_y = source.set_index('uid')[y_point_label].to_dict() a_vals, x1, x2, y1, y2 = zip(*[(a, node_x[a], node_x[b], node_y[a], node_y[b]) for a, bs in zip(nodes, edges) for b in bs]) x1, x2, y1, y2 = array(x1), array(x2), array(y1), array(y2) x3 = (x1 + x2) / 2 y3 = (y1 + y2) / 2 xc = x3 + abs(y3 - y2) yc = y3 + abs(x3 - x2) new_source = { 'x1': x1, 'x2': x2, 'xc': xc, 'y1': y1, 'y2': y2, 'yc': yc } for c in source.columns: if (c not in self.omit_columns) and c not in new_source: col_dict = source.set_index('uid', drop=False)[c].to_dict() new_source[c] = [col_dict[v] for v in a_vals] else: raise ValueError( 'Values of `links` field must be numeric or a list, set, or tuple of values from the `uid` field.' ) if 'color' in kwargs.keys(): color = kwargs.pop('color') for v in Quadratic.dataspecs(): if 'color' in v: kwargs[v] = color if 'alpha' in kwargs.keys(): alpha = kwargs.pop('alpha') for v in Quadratic.dataspecs(): if 'alpha' in v: kwargs[v] = alpha if edge_type == 'curved': model_object = Quadratic(x0='x1', y0='y1', x1="x2", y1="y2", cx="xc", cy="yc", name=source_label, **kwargs) elif edge_type == 'straight': model_object = Segment(x0='x1', y0='y1', x1="x2", y1="y2", name=source_label, **kwargs) else: raise ValueError( 'Keyword `edge_type` must be either "curved" or "straight".') source = ColumnDataSource(new_source, name=source_label) rend = self.plot.add_glyph(source, model_object) if legend is not None: li = LegendItem(label=legend, renderers=[rend]) self.legend.items.append(li) if tooltips is not None: self.plot.add_tools(HoverTool(tooltips=tooltips, renderers=[rend])) return self
def add_layer(self, source_label, bokeh_model='MultiPolygons', tooltips=None, legend=None, click_for_map=None, start_hex='#ff0000', end_hex='#0000ff', mid_hex='#ffffff', color_transform=None, **kwargs): """ Add bokeh models (glyphs or markers) to `self.plot`. `self.prepare_plot` must have been called previous to this. Parameters: source_label (str): string corresponding to a label previously called in `self.add_source` bokeh_model: any Bokeh model or glyph class tooltips: string or list of tuples (passed to Bokeh HoverTool) legend (str): name to assign to this layer in the plot legend start_hex (str): the color with which to start a color gradient end_hex (str): the color with which to end a color gradient mid_hex (str): the color to use for zero in a color gradient if the basis of that gradient contains both positive and negative values color_transform (callable): any function that can transform a numpy array (log, log10, etc.) kwargs: options passed to the objected for `bokeh_model` This method allows two special kwargs: 'color' and 'alpha'. When used with a bokeh model that has 'fill_color' and 'line_color' and 'fill_alpha' and 'line_alpha' properties, calling the special kwarg will use the same value for both. """ self._validate_workflow('add_layer') if self.plot is None: raise AssertionError( 'self.plot is null; call `self.prepare_plot`.') if bokeh_model not in bokeh_utils.MODELS: raise ValueError('Valid values for `bokeh_model` are: {}'.format( ', '.join([repr(x) for x in bokeh_utils.MODELS.keys()]))) bokeh_model = bokeh_utils.MODELS[bokeh_model] kwargs, hover_kwargs, new_fields, colorbar = bokeh_utils.prepare_properties( bokeh_model, kwargs, self.sources[source_label], bar_height=self.plot.frame_height, start_hex=start_hex, end_hex=end_hex, mid_hex=mid_hex, color_transform=color_transform, ) if self.colorbar is None: self.colorbar = colorbar source = self.columndatasources[source_label] for k, v in new_fields.items(): self.columndatasources[source_label].data[k] = v hover_object = None if bokeh_model == bokeh_utils.MODELS['MultiPolygons']: if type(self.plot) == GMapPlot: raise ValueError( 'The `MultiPolygon` glyph cannot yet be used with a Google Maps plot.' ) model_object = bokeh_model(xs='xsf', ys='ysf', name=source_label, **kwargs) if len(hover_kwargs) > 0: for k, v in hover_kwargs.items(): kwargs[k] = v hover_object = bokeh_model(xs='xsf', ys='ysf', name=source_label, **kwargs) if 'f' in self.remove_columns[source_label]: _ = self.remove_columns[source_label].pop( self.remove_columns[source_label].index('f')) else: model_object = bokeh_model(x='xsp', y='ysp', name=source_label, **kwargs) if len(hover_kwargs) > 0: for k, v in hover_kwargs: kwargs[k] = v hover_object = bokeh_model(xs='xsf', ys='ysf', name=source_label, **kwargs) if 'p' in self.remove_columns[source_label]: _ = self.remove_columns[source_label].pop( self.remove_columns[source_label].index('p')) if source_label in self.views: if len(hover_kwargs) > 0: rend = self.plot.add_glyph(source, model_object, hover_glyph=hover_object, view=self.views[source_label]) else: rend = self.plot.add_glyph(source, model_object, view=self.views[source_label]) else: if len(hover_kwargs) > 0: rend = self.plot.add_glyph(source, model_object, hover_glyph=hover_object) else: rend = self.plot.add_glyph(source, model_object) if legend is not None: li = LegendItem(label=legend, renderers=[rend]) self.legend.items.append(li) if tooltips is not None: if tooltips == 'all': tooltips = [(k, '@{}'.format(k)) for k in source.data.keys() if k not in ('xsf', 'ysf', 'xsp', 'ysp')] elif tooltips == 'point': tooltips = [(k, '@{}'.format(k)) for k in source.data.keys() if k not in ('xsf', 'ysf', 'xsp', 'ysp', 'raw_data')] elif tooltips == 'raw_data': tooltips = [(k, '@{}'.format(k)) for k in source.data.keys() if k not in ('xsf', 'ysf', 'xsp', 'ysp', 'x_coord_point', 'y_coord_point')] elif tooltips == 'meta': tooltips = [ (k, '@{}'.format(k)) for k in source.data.keys() if k not in ('xsf', 'ysf', 'xsp', 'ysp', 'x_coord_point', 'y_coord_point', 'raw_data') ] self.plot.add_tools(HoverTool(tooltips=tooltips, renderers=[rend])) if click_for_map is not None: taptool = self.plot.select(type=TapTool) if click_for_map == 'google': url = 'https://maps.google.com/maps?q=@y_coord_point,@x_coord_point' taptool.callback = OpenURL(url=url) elif click_for_map == 'bing': url = 'https://bing.com/maps/default.aspx?sp=point.' url += '@{y_coord_point}_@{x_coord_point}_Selected point&style=r' taptool.callback = OpenURL(url=url) else: raise NotImplementedError( 'Value for `click_for_map` must be "bing", "google" or None.' ) self.validation['add_layer'] = True return self
cmplot = figure(plot_width=500, plot_height=400, y_range=(0, 13), name="cmplot") cmplot.vbar_stack(stackers=['mc', 'fp', 'nd'], x='yr', source=cmdata, color=("green", "yellow", "red"), width=0.5, line_color="black") cmplot.toolbar_location = None cmplot.xaxis.axis_label = "Year" cmplot.yaxis.axis_label = "Number of Meetings" li1 = LegendItem(label=' Done', renderers=[cmplot.renderers[0]]) li2 = LegendItem(label=' Planed', renderers=[cmplot.renderers[1]]) li3 = LegendItem(label='Not Done', renderers=[cmplot.renderers[2]]) legend1 = Legend(items=[li1, li2, li3], location='bottom_center') cmplot.add_layout(legend1) new_legend = cmplot.legend[0] cmplot.add_layout(new_legend, 'below') cmplot.legend.orientation = "horizontal" #show(cmplot) #curdoc().add_root(cmplot) #### teacher qualification #### sheet_teach = client.open("teacher_qualification").sheet1
def lengths(raw_counts, group_by='experiment', groupings=None, initial_top_group_selections=None, initial_sub_group_selections=None, types=None, max_length=1000, x_max=500, ): ys = { 'raw_counts': raw_counts, 'by_type': {}, 'by_exp': {}, } if types is None: types = raw_counts.values()[0].keys() for exp_name in ys['raw_counts']: total_counts = sum(ys['raw_counts'][exp_name]['insert_lengths']) ys['by_type'][exp_name] = {} ys['by_exp'][exp_name] = {} for type_name in raw_counts[exp_name]: raw = raw_counts[exp_name][type_name] ys['by_type'][exp_name][type_name] = np.true_divide(raw, sum(raw)) ys['by_exp'][exp_name][type_name] = np.true_divide(raw, total_counts) first_exp_name = ys['raw_counts'].keys()[0] # ys has experiments as top level keys and types as lower level keys. # Need a dictionary with checkbox names as the top keys, so if grouping by # type, don't need to do anything. If grouping by experiment, need to invert # the order. # 2017_03_06: this comment doesnt seem to make sense. if group_by == 'experiment': menu_options = sorted(ys['raw_counts']) checkbox_names = sorted(ys['raw_counts'][first_exp_name]) rekeyed_ys = {} for normalization in ys: rekeyed_ys[normalization] = {} for checkbox_name in checkbox_names: rekeyed_ys[normalization][checkbox_name] = {} for menu_name in menu_options: rekeyed_ys[normalization][checkbox_name][menu_name] = ys[normalization][menu_name][checkbox_name] ys = rekeyed_ys elif group_by == 'type': menu_options = types checkbox_names = sorted(ys['raw_counts']) if initial_sub_group_selections is None: initial_sub_group_selections = checkbox_names if initial_top_group_selections is None: initial_top_group_selections = [] if groupings is None: groupings = {n: [n] for n in checkbox_names} colors = dict(zip(checkbox_names, cycle(colors_list))) initial_menu_selection = None if initial_menu_selection is None: initial_menu_selection = menu_options[0] if initial_menu_selection not in menu_options: raise ValueError('{0} not in {1}'.format(initial_menu_selection, menu_options)) sources = {} for key in ['plotted'] + ys.keys(): if key == 'plotted': normalization = 'raw_counts' else: normalization = key sources[key] = {} for checkbox_name in checkbox_names: data = {} for menu_name in ys[normalization][checkbox_name]: full_list = list(ys[normalization][checkbox_name][menu_name]) if len(full_list) > max_length: full_list[max_length] = sum(full_list[max_length:]) elif len(full_list < max_length): full_list.extend([0]*(max_length + 1 - len(full_list))) data[menu_name] = full_list[:max_length + 1] source = bokeh.models.ColumnDataSource(data) xs = range(max_length + 1) source.data['x'] = xs source.data['y'] = source.data[initial_menu_selection] source.data['name'] = [checkbox_name] * len(xs) source.name = 'source_{0}_{1}'.format(checkbox_name, key) sources[key][checkbox_name] = source tools = [ 'pan', 'tap', 'box_zoom', 'wheel_zoom', 'save', 'reset', 'undo', ] fig = bokeh.plotting.figure(plot_width=1200, plot_height=800, tools=tools, active_scroll='wheel_zoom', name='figure', ) fig.grid.grid_line_alpha = 0.4 fig.y_range.name = 'y_range' fig.x_range.name = 'x_range' fig.x_range.end = x_max range_callback = build_callback('lengths_range') fig.y_range.callback = range_callback fig.x_range.callback = range_callback fig.xaxis.axis_label = 'Length' fig.yaxis.axis_label = 'y' fig.xaxis.name = 'x_axis' fig.yaxis.name = 'y_axis' fig.xaxis.axis_label_text_font_style = 'normal' fig.yaxis.axis_label_text_font_style = 'normal' legend_items = [] initial_legend_items = [] lines = [] for checkbox_name, source in sources['plotted'].items(): if checkbox_name in initial_sub_group_selections: color = colors[checkbox_name] line_width = 2 line_alpha = 0.95 circle_visible = True else: color = colors[checkbox_name] line_width = 1 circle_visible = False if len(initial_sub_group_selections) > 0: line_alpha = unselected_alpha else: line_alpha = 0.6 line = fig.line(x='x', y='y', color=color, source=source, line_width=line_width, line_alpha=line_alpha, line_join='round', nonselection_line_color=colors[checkbox_name], nonselection_line_alpha=0.5, hover_alpha=1.0, hover_color=colors[checkbox_name], ) line.hover_glyph.line_width = 3 line.name = 'line_{0}'.format(checkbox_name) lines.append(line) circle = fig.circle(x='x', y='y', color=colors[checkbox_name], source=source, size=1, fill_alpha=0.95, line_alpha=0.95, visible=circle_visible, hover_alpha=0.95, hover_color=colors[checkbox_name], ) circle.hover_glyph.visible = True circle.name = 'circle_{0}'.format(checkbox_name) legend_item = LegendItem(label=checkbox_name, renderers=[line]) legend_items.append(legend_item) if checkbox_name in initial_sub_group_selections: initial_legend_items.append(legend_item) legend = bokeh.models.Legend(name='legend', items=legend_items, ) fig.add_layout(legend) source_callback = build_callback('metacodon_selection') for source in sources['plotted'].values(): source.callback = source_callback hover = bokeh.models.HoverTool(line_policy='interp', renderers=lines, ) hover.tooltips = [('name', '@name')] fig.add_tools(hover) menu = bokeh.models.widgets.MultiSelect(options=menu_options, value=[initial_menu_selection], size=min(40, len(menu_options)), ) menu.callback = build_callback('metacodon_menu') sub_group_callback = build_callback('metacodon_sub_group', format_kwargs=dict(color_unselected='true'), ) top_group_callback = build_callback('metacodon_top_group') top_groups = [] sub_groups = [] width = 100 for top_name, sub_names in sorted(groupings.items()): width = 75 + max(len(l) for l in sub_names) * 6 top_active = [0] if top_name in initial_top_group_selections else [] top = bokeh.models.widgets.CheckboxGroup(labels=[top_name], active=top_active, width=width, name='top_{0}'.format(top_name), callback=top_group_callback, ) top_groups.append(top) sub_active = [i for i, n in enumerate(sub_names) if n in initial_sub_group_selections] sub = bokeh.models.widgets.CheckboxGroup(labels=sorted(sub_names), active=sub_active, width=width, callback=sub_group_callback, name='sub_{0}'.format(top_name), ) sub_groups.append(sub) alpha_slider = bokeh.models.Slider(start=0., end=1., value=0.5, step=.05, title='alpha', ) alpha_slider.callback = build_callback('lengths_unselected_alpha') highest_level_chooser = bokeh.models.widgets.Select(options=ys.keys(), value='raw_counts', ) injection_sources = [] for key in ys: injection_sources.extend(sources[key].values()) # Note: use throwaway names instead of source names to ensure no illegal # characters in names. injection = {'ensure_no_collision_{0}'.format(i): v for i, v in enumerate(injection_sources)} highest_level_chooser.callback = build_callback('metacodon_highest_level', args=injection, ) grid = [ top_groups, sub_groups, [fig, bokeh.layouts.widgetbox([menu, highest_level_chooser, alpha_slider])], ] bokeh.io.show(bokeh.layouts.layout(grid))