def visualize_nodes(labels: list, pca_2d: list, num_of_clusters: int, features: list, albums: list, task_name: str, method: str): color_dic = dict() for i in range(-1, num_of_clusters + 1): color_dic[i] = random_color() cls = [color_dic[label] for label in labels] TOOLTIPS = [('title', '@title'), ('URL', '@url'), ('Cluster', '@cluster')] source = ColumnDataSource( data=dict(x=pca_2d[:, 0], y=pca_2d[:, 1], title=[album['title'] for album in albums], url=[album['url'] for album in albums], color=cls, cluster=labels)) for feature in features: TOOLTIPS.append((feature, "@{0}".format(feature))) source.add([album[feature] for album in albums], feature) p = figure(sizing_mode='stretch_both', title=method + " clustering of albums on metrics: " + ", ".join(features), tooltips=TOOLTIPS, output_backend='webgl') p.circle('x', 'y', source=source, color='color', fill_alpha=0.2, size=5) output_file(get_local_data_path(task_name + ".html"), title="PSZ | {0} clustering of Albums".format(method)) show(p)
def ridgeplot(courses_obj): # first format 'courses_obj' into 'probly' DataFrame format # courses_obj: [{'course_name': 'Calculus...', ...}, ...] grades = [[100*y[2]/y[3] for y in x['assignments']] for x in courses_obj] # turn this list of lists into a complete NumPy array length = len(sorted(grades, key=len, reverse=True)[0]) grades = np.array([xi+[None]*(length-len(xi)) for xi in grades], dtype='float') columns = [x['course_name'] for x in courses_obj] grades = grades.transpose() probly = pd.DataFrame(grades, columns=columns) cats = list(reversed(probly.keys())) palette = [cc.rainbow[i*15] for i in range(17)] x = np.linspace(-20,110, 500) source = ColumnDataSource(data=dict(x=x)) p = figure(y_range=cats, plot_width=900, plot_height = 300, x_range=(-5, 120))#, toolbar_location=None) for i, cat in enumerate(reversed(cats)): adjusted = probly[cat].replace([np.inf, -np.inf], np.nan).dropna(how="all") if adjusted.size == 1 or pd.unique(adjusted).size == 1: # this means we can't compute continue pdf = gaussian_kde(adjusted) #p = figure(plot_width=400, plot_height=400) #p.line(x, pdf(x)) y = ridge(cat, pdf(x), scale=2) #p.line(x, y, color='black') #show(p) source.add(y, cat) p.patch('x', cat, color=palette[i], alpha=0.6, line_color="black", source=source) p.outline_line_color = None p.background_fill_color = "#efefef" p.tools = [PanTool(), CrosshairTool(), HoverTool(), SaveTool(), BoxZoomTool(), WheelZoomTool(), ResetTool()] ticks = list(np.array([np.array([0,3,7])+i*10 for i in range(10)]).flatten()) + [100] p.xaxis.ticker = FixedTicker(ticks=ticks) p.xaxis.formatter = PrintfTickFormatter(format="%d") p.xaxis.axis_label = "Your Grade Distribution" p.yaxis.axis_label = "Your Courses" p.ygrid.grid_line_color = None p.xgrid.grid_line_color = "Grey" p.xgrid.ticker = p.xaxis[0].ticker p.axis.minor_tick_line_color = None p.axis.major_tick_line_color = None p.axis.axis_line_color = None p.y_range.range_padding = 0.12 return p
def pnl_chart(df): """ Creates a chart to display P&L Parameters ---------- df : DataFrame, with the following columns: Date, Realized, Unrealized, Total Returns ------- Figure """ source = ColumnDataSource(df) source.add(df.index.map(lambda d: d.strftime('%d-%b-%y')), 'DateStr') title = Title(text='Historical Daily P&L', text_font_size='14pt', align='center') p = figure(x_axis_type='datetime', sizing_mode='scale_height', title=title) p.xaxis.formatter = DatetimeTickFormatter(days=['%d-%b-%y'], months=['%d-%b-%y'], years=['%d-%b-%y']) p.xaxis.major_label_text_font_size = '10pt' p.yaxis.formatter = NumeralTickFormatter(format=',') p.yaxis.major_label_text_font_size = '10pt' for col, color in zip(['Realized', 'Unrealized', 'Total'], Spectral4): p.line(x='Date', y=col, source=source, line_width=2, color=color, alpha=0.8, legend_label=col) hover = HoverTool(tooltips=[ ('Date', '@DateStr'), ('Realized', '@Realized{,}'), ('Unrealized', '@Unrealized{,}'), ('Total', '@Total{,}'), ], formatters={ 'Realized': 'printf', 'Unrealized': 'printf', 'Total': 'printf' }, mode='vline', renderers=[p.renderers[-1]]) p.add_tools(hover) p.legend.location = "top_left" p.legend.click_policy = "hide" return p
def _to_cds(data, unstack=None): """""" source_dict = {} if hasattr(data, "data_vars"): for v in data.data_vars: source_dict.update(_dict_from_da(data[v], unstack)) else: source_dict.update(_dict_from_da(data, unstack)) source = ColumnDataSource(source_dict) for c in data.coords: if c not in data.dims and data[c].ndim == 1: source.add(data[c].values, c) return source
class PlotterBokeh(Plotter): def __init__(self, ds, x, y, z=None, **kwargs): """ """ # bokeh custom options / defaults kwargs['return_fig'] = kwargs.pop('return_fig', False) self._interactive = kwargs.pop('interactive', False) super().__init__(ds, x, y, z, **kwargs, backend='BOKEH') def prepare_axes(self): """Make the bokeh plot figure and set options. """ from bokeh.plotting import figure if self.add_to_axes is not None: self._plot = self.add_to_axes else: # Currently axes scale type must be set at figure creation? self._plot = figure( # convert figsize to roughly matplotlib dimensions width=int(self.figsize[0] * 80 + (100 if self._use_legend else 0) + (20 if self._ytitle else 0) + (20 if not self.yticklabels_hide else 0)), height=int(self.figsize[1] * 80 + (20 if self.title else 0) + (20 if self._xtitle else 0) + (20 if not self.xticklabels_hide else 0)), x_axis_type=('log' if self.xlog else 'linear'), y_axis_type=('log' if self.ylog else 'linear'), y_axis_location=('right' if self.ytitle_right else 'left'), title=self.title, toolbar_location="above", toolbar_sticky=False, active_scroll="wheel_zoom", ) def set_axes_labels(self): """Set the labels on the axes. """ if self._xtitle: self._plot.xaxis.axis_label = self._xtitle if self._ytitle: self._plot.yaxis.axis_label = self._ytitle def set_axes_range(self): """Set the plot ranges of the axes, and the panning limits. """ from bokeh.models import DataRange1d self.calc_data_range() # plt_x_centre = (self._data_xmax + self._data_xmin) / 2 # plt_x_range = self._data_xmax - self._data_xmin # xbounds = (plt_x_centre - plt_x_range, plt_x_centre + plt_x_range) xbounds = None self._plot.x_range = (DataRange1d( start=self._xlims[0], end=self._xlims[1], bounds=xbounds) if self._xlims else DataRange1d(bounds=xbounds)) # plt_y_centre = (self._data_ymax + self._data_ymin) / 2 # plt_y_range = abs(self._data_ymax - self._data_ymin) # ybounds = (plt_y_centre - plt_y_range, plt_y_centre + plt_y_range) ybounds = None self._plot.y_range = (DataRange1d( start=self._ylims[0], end=self._ylims[1], bounds=ybounds) if self._ylims else DataRange1d(bounds=ybounds)) def set_spans(self): """Set custom horizontal and verical line spans. """ from bokeh.models import Span span_opts = { 'level': 'glyph', 'line_dash': 'dashed', 'line_color': (127, 127, 127), 'line_width': self.span_width, } if self.hlines: for hl in self.hlines: self._plot.add_layout( Span(location=hl, dimension='width', **span_opts)) if self.vlines: for vl in self.vlines: self._plot.add_layout( Span(location=vl, dimension='height', **span_opts)) def set_gridlines(self): """Set whether to use gridlines or not. """ if not self.gridlines: self._plot.xgrid.visible = False self._plot.ygrid.visible = False else: self._plot.xgrid.grid_line_dash = self.gridline_style self._plot.ygrid.grid_line_dash = self.gridline_style def set_tick_marks(self): """Set custom locations for the tick marks. """ from bokeh.models import FixedTicker if self.xticks: self._plot.xaxis[0].ticker = FixedTicker(ticks=self.xticks) if self.yticks: self._plot.yaxis[0].ticker = FixedTicker(ticks=self.yticks) if self.xticklabels_hide: self._plot.xaxis.major_label_text_font_size = '0pt' if self.yticklabels_hide: self._plot.yaxis.major_label_text_font_size = '0pt' def set_sources_heatmap(self): from bokeh.plotting import ColumnDataSource # initialize empty source if not hasattr(self, '_source'): self._source = ColumnDataSource(data=dict()) # remove mask from data -> not necessary soon? / convert to nan? var = np.ma.getdata(self._heatmap_var) self._source.add([var], 'image') self._source.add([self._data_xmin], 'x') self._source.add([self._data_ymin], 'y') self._source.add([self._data_xmax - self._data_xmin], 'dw') self._source.add([self._data_ymax - self._data_ymin], 'dh') def set_sources(self): """Set the source dictionaries to be used by the plotter functions. This is seperate to allow interactive updates of the data only. """ from bokeh.plotting import ColumnDataSource # check if heatmap if hasattr(self, '_heatmap_var'): return self.set_sources_heatmap() # 'copy' the zlabels iterator into src_zlbs self._zlbls, src_zlbs = itertools.tee(self._zlbls) # Initialise with empty dicts if not hasattr(self, "_sources"): self._sources = [ ColumnDataSource(dict()) for _ in range(len(self._z_vals)) ] # range through all data and update the sources for i, (zlabel, data) in enumerate(zip(src_zlbs, self._gen_xy())): self._sources[i].add(data['x'], 'x') self._sources[i].add(data['y'], 'y') self._sources[i].add([zlabel] * len(data['x']), 'z_coo') # check for color for scatter plot if 'c' in data: self._sources[i].add(data['c'], 'c') # check if should set y_err as well if 'ye' in data: y_err_p = data['y'] + data['ye'] y_err_m = data['y'] - data['ye'] self._sources[i].add(list(zip(data['x'], data['x'])), 'y_err_xs') self._sources[i].add(list(zip(y_err_p, y_err_m)), 'y_err_ys') # check if should set x_err as well if 'xe' in data: x_err_p = data['x'] + data['xe'] x_err_m = data['x'] - data['xe'] self._sources[i].add(list(zip(data['y'], data['y'])), 'x_err_ys') self._sources[i].add(list(zip(x_err_p, x_err_m)), 'x_err_xs') def plot_legend(self, legend_items=None): """Add a legend to the plot. """ if self._use_legend: from bokeh.models import Legend loc = {'best': 'top_left'}.get(self.legend_loc, self.legend_loc) where = {None: 'right'}.get(self.legend_where, self.legend_where) # might be manually specified, e.g. from multiplot if legend_items is None: legend_items = self._lgnd_items lg = Legend(items=legend_items) lg.location = loc lg.click_policy = 'hide' self._plot.add_layout(lg, where) # Don't repeatedly redraw legend self._use_legend = False def set_mappable(self): from bokeh.models import LogColorMapper, LinearColorMapper import matplotlib as plt mappr_fn = (LogColorMapper if self.colormap_log else LinearColorMapper) bokehpalette = [plt.colors.rgb2hex(m) for m in self.cmap(range(256))] self.mappable = mappr_fn(palette=bokehpalette, low=self._zmin, high=self._zmax) def plot_colorbar(self): if self._use_colorbar: where = {None: 'right'}.get(self.legend_where, self.legend_where) from bokeh.models import ColorBar, LogTicker, BasicTicker ticker = LogTicker if self.colormap_log else BasicTicker color_bar = ColorBar(color_mapper=self.mappable, location=(0, 0), ticker=ticker(desired_num_ticks=6), title=self._ctitle) self._plot.add_layout(color_bar, where) def set_tools(self): """Set which tools appear for the plot. """ from bokeh.models import HoverTool self._plot.add_tools( HoverTool(tooltips=[("({}, {})".format( self.x_coo, self.y_coo if isinstance(self.y_coo, str) else None), "(@x, @y)"), (self.z_coo, "@z_coo")])) def update(self): from bokeh.io import push_notebook self.set_sources() push_notebook() def show(self, **kwargs): """Show the produced figure. """ if self.return_fig: return self._plot bshow(self._plot, **kwargs) return self def prepare_plot(self): self.prepare_axes() self.set_axes_labels() self.set_axes_range() self.set_spans() self.set_gridlines() self.set_tick_marks() self.set_sources()
from bokeh.plotting import ColumnDataSource, figure, gridplot, output_file, show from bokeh.sampledata.autompg import autompg output_file("panning.html") # Load some Automobile data into a data source. Interesting columns are: # "yr" - Year manufactured # "mpg" - miles per gallon # "displ" - engine displacement # "hp" - engine horsepower # "cyl" - number of cylinders source = ColumnDataSource(autompg.to_dict("list")) source.add(autompg["yr"], name="yr") # Let's set up some plot options in a dict that we can re-use on multiple plots plot_config = dict(plot_width=300, plot_height=300, tools="pan,wheel_zoom,box_zoom,select") # First let's plot the "yr" vs "mpg" using the plot config above # Note that we are supplying our our data source to the renderer explicitly p1 = figure(title="MPG by Year", **plot_config) p1.circle("yr", "mpg", color="blue", source=source) # EXERCISE: make another figure p2 with circle renderer, for "hp" vs "displ" with # color "green". This renderer should use the same data source as the renderer # above, that is what will cause the plots selections to be linked p2 = figure(title="HP vs. Displacement", **plot_config) p2.circle("hp", "displ", color="green", source=source) # EXERCISE: and another figure p3 with circle renderer for "mpg" vs "displ",
from bokeh.plotting import ColumnDataSource, figure, gridplot, output_file, show from bokeh.sampledata.autompg import autompg output_file("brushing.html") # Load some Automobile data into a data source. Interesting columns are: # "yr" - Year manufactured # "mpg" - miles per gallon # "displ" - engine displacement # "hp" - engine horsepower # "cyl" - number of cylinders source = ColumnDataSource(autompg.to_dict("list")) source.add(autompg["yr"], name="yr") # define some tools to add TOOLS = "pan,wheel_zoom,box_zoom,box_select,lasso_select"# Let's set up some plot options in a dict that we can re-use on multiple plots # Let's set up some plot options in a dict that we can re-use on multiple plots plot_config = dict(plot_width=300, plot_height=300, tools=TOOLS) # First let's plot the "yr" vs "mpg" using the plot config above # Note that we are supplying our our data source to the renderer explicitly p1 = figure(title="MPG by Year", **plot_config) p1.circle("yr", "mpg", color="blue", source=source) # EXERCISE: make another figure p2 with circle renderer, for "hp" vs "displ" with # color "green". This renderer should use the same data source as the renderer # above, that is what will cause the plots selections to be linked p2 = figure(title="HP vs. Displacement", **plot_config) p2.circle("hp", "displ", color="green", source=source)
def _plot_get_source(self, conf_list, runs, X, inc_list, hp_names): """ Create ColumnDataSource with all the necessary data Contains for each configuration evaluated on any run: - all parameters and values - origin (if conflicting, origin from best run counts) - type (default, incumbent or candidate) - # of runs - size - color Parameters ---------- conf_list: list[Configuration] configurations runs: list[int] runs per configuration (same order as conf_list) X: np.array configuration-parameters as 2-dimensional array inc_list: list[Configuration] incumbents for this conf-run hp_names: list[str] names of hyperparameters Returns ------- source: ColumnDataSource source with attributes as requested """ # Remove all configurations without any runs keep = [i for i in range(len(runs)) if runs[i] > 0] runs = np.array(runs)[keep] conf_list = np.array(conf_list)[keep] X = X[keep] source = ColumnDataSource(data=dict(x=X[:, 0], y=X[:, 1])) for k in hp_names: # Add parameters for each config source.add([c[k] if c[k] else "None" for c in conf_list], escape_parameter_name(k)) default = conf_list[0].configuration_space.get_default_configuration() conf_types = [ "Default" if c == default else "Final Incumbent" if c == inc_list[-1] else "Incumbent" if c in inc_list else "Candidate" for c in conf_list ] # We group "Local Search" and "Random Search (sorted)" both into local origins = [self._get_config_origin(c) for c in conf_list] source.add(conf_types, 'type') source.add(origins, 'origin') sizes = self._get_size(runs) sizes = [ s * 3 if conf_types[idx] == "Default" else s for idx, s in enumerate(sizes) ] source.add(sizes, 'size') source.add(self._get_color(source.data['type']), 'color') source.add(runs, 'runs') # To enforce zorder, we categorize all entries according to their size # Since we plot all different zorder-levels sequentially, we use a # manually defined level of influence num_bins = 20 # How fine-grained the size-ordering should be min_size, max_size = min(source.data['size']), max(source.data['size']) step_size = (max_size - min_size) / num_bins if step_size == 0: step_size = 1 zorder = [ str(int((s - min_size) / step_size)) for s in source.data['size'] ] source.add(zorder, 'zorder') # string, so we can apply group filter return source
def plot_interactive_footprint(self): """Use bokeh to create an interactive algorithm footprint with zoom and hover tooltips. Should avoid problems with overplotting (since we can zoom) and provide better information about instances.""" features = np.array(self.features_2d) instances = self.insts runhistory = self.rh algo = {v: k for k, v in self.algo_name.items()} incumbent = algo['incumbent'] default = algo['default'] source = ColumnDataSource(data=dict(x=features[:, 0], y=features[:, 1])) # Add all necessary information for incumbent and default source.add(instances, 'instance_name') instance_set = [ 'train' if i in self.train_feats.keys() else 'test' for i in instances ] source.add(instance_set, 'instance_set') # train or test for config, name in [(incumbent, 'incumbent'), (default, 'default')]: cost = get_cost_dict_for_config(runhistory, config) source.add([cost[i] for i in instances], '{}_cost'.format(name)) # TODO should be in function good, bad = self._get_good_bad(config) color = [ 1 if idx in good else 0 for idx, i in enumerate(instances) ] # TODO end color = ['blue' if c else 'red' for c in color] self.logger.debug("%s colors: %s", name, str(color)) source.add(color, '{}_color'.format(name)) source.add(source.data['default_color'], 'color') # Define what appears in tooltips hover = HoverTool(tooltips=[ ('instance name', '@instance_name'), ('def cost', '@default_cost'), ('inc_cost', '@incumbent_cost'), ('set', '@instance_set'), ]) # Add radio-button def_inc_callback = CustomJS(args=dict(source=source), code=""" var data = source.data; if (cb_obj.active == 0) { data['color'] = data['default_color']; } else { data['color'] = data['incumbent_color']; } source.change.emit(); """) def_inc_radio_button = RadioButtonGroup( labels=["default", "incumbent"], active=0, callback=def_inc_callback) # Plot x_range = DataRange1d(bounds='auto', start=min(features[:, 0]) - 1, end=max(features[:, 0]) + 1) y_range = DataRange1d(bounds='auto', start=min(features[:, 1]) - 1, end=max(features[:, 1]) + 1) p = figure( plot_height=500, plot_width=600, tools=[hover, 'save', 'wheel_zoom', 'box_zoom', 'pan', 'reset'], active_drag='box_zoom', x_range=x_range, y_range=y_range) # Scatter train and test individually to toggle them train_view = CDSView( source=source, filters=[GroupFilter(column_name='instance_set', group='train')]) test_view = CDSView( source=source, filters=[GroupFilter(column_name='instance_set', group='test')]) train = p.scatter(x='x', y='y', source=source, view=train_view, color='color') test = p.scatter(x='x', y='y', source=source, view=test_view, color='color') p.xaxis.axis_label, p.yaxis.axis_label = 'principal component 1', 'principal component 2' p.xaxis.axis_label_text_font_size = p.yaxis.axis_label_text_font_size = "15pt" train_test_callback = CustomJS(args=dict(source=source, train_view=train, test_view=test), code=""" var data = source.data; if (cb_obj.active == 0) { train_view.visible = true; test_view.visible = true; } else if (cb_obj.active == 1) { train_view.visible = true; test_view.visible = false; } else { train_view.visible = false; test_view.visible = true; } """) train_test_radio_button = RadioButtonGroup( labels=["all", "train", "test"], active=0, callback=train_test_callback) # Export and return if self.output_dir: path = os.path.join(self.output_dir, "content/images/algorithm_footprint.png") export_bokeh(p, path, self.logger) layout = column( p, row(widgetbox(def_inc_radio_button), widgetbox(train_test_radio_button))) return layout
from bokeh.models import HoverTool from bokeh.models.widgets import DateRangeSlider data = pd.read_csv('Lekagul Sensor Data.csv') data_car_id = data['car-id'].as_matrix() data.columns plot_width = 1100 plot_height = 500 width = 1 hover = HoverTool() hover.tooltips = [('index', '$index')] all_gate_names = np.unique(data['gate-name'].as_matrix()) all_car_types = np.unique(data['car-type'].as_matrix()) source = ColumnDataSource() source.add(all_gate_names, name='Gate Names') legend_var = [ '2 axle car (or motorcycle)', '2 axle Truck', 'Ranger', '3 axle Truck', '4 axle (and above) Truck', '2 axle Bus', '3 axle Bus' ] start_y = '2016' start_m = '02' start_d = '10' end_y = '2016' end_m = '04' end_d = '20' start_mask = '2016-02-10' end_mask = '2016-04-20' mask = (data['Timestamp'] >= start_mask) & (data['Timestamp'] <= end_mask)
class PlotterBokeh(Plotter): def __init__(self, ds, x, y, z=None, **kwargs): """ """ # bokeh custom options / defaults kwargs['return_fig'] = kwargs.pop('return_fig', False) self._interactive = kwargs.pop('interactive', False) super().__init__(ds, x, y, z, **kwargs, backend='BOKEH') def prepare_axes(self): """Make the bokeh plot figure and set options. """ from bokeh.plotting import figure if self.add_to_axes is not None: self._plot = self.add_to_axes else: # Currently axes scale type must be set at figure creation? self._plot = figure( # convert figsize to roughly matplotlib dimensions width=int(self.figsize[0] * 80 + (100 if self._use_legend else 0) + (20 if self._ytitle else 0) + (20 if not self.yticklabels_hide else 0)), height=int(self.figsize[1] * 80 + (20 if self.title else 0) + (20 if self._xtitle else 0) + (20 if not self.xticklabels_hide else 0)), x_axis_type=('log' if self.xlog else 'linear'), y_axis_type=('log' if self.ylog else 'linear'), y_axis_location=('right' if self.ytitle_right else 'left'), title=self.title, toolbar_location="above", toolbar_sticky=False, active_scroll="wheel_zoom", ) def set_axes_labels(self): """Set the labels on the axes. """ if self._xtitle: self._plot.xaxis.axis_label = self._xtitle if self._ytitle: self._plot.yaxis.axis_label = self._ytitle def set_axes_range(self): """Set the plot ranges of the axes, and the panning limits. """ from bokeh.models import DataRange1d self.calc_data_range() # plt_x_centre = (self._data_xmax + self._data_xmin) / 2 # plt_x_range = self._data_xmax - self._data_xmin # xbounds = (plt_x_centre - plt_x_range, plt_x_centre + plt_x_range) xbounds = None self._plot.x_range = (DataRange1d(start=self._xlims[0], end=self._xlims[1], bounds=xbounds) if self._xlims else DataRange1d(bounds=xbounds)) # plt_y_centre = (self._data_ymax + self._data_ymin) / 2 # plt_y_range = abs(self._data_ymax - self._data_ymin) # ybounds = (plt_y_centre - plt_y_range, plt_y_centre + plt_y_range) ybounds = None self._plot.y_range = (DataRange1d(start=self._ylims[0], end=self._ylims[1], bounds=ybounds) if self._ylims else DataRange1d(bounds=ybounds)) def set_spans(self): """Set custom horizontal and verical line spans. """ from bokeh.models import Span span_opts = { 'level': 'glyph', 'line_dash': 'dashed', 'line_color': (127, 127, 127), 'line_width': self.span_width, } if self.hlines: for hl in self.hlines: self._plot.add_layout(Span( location=hl, dimension='width', **span_opts)) if self.vlines: for vl in self.vlines: self._plot.add_layout(Span( location=vl, dimension='height', **span_opts)) def set_gridlines(self): """Set whether to use gridlines or not. """ if not self.gridlines: self._plot.xgrid.visible = False self._plot.ygrid.visible = False else: self._plot.xgrid.grid_line_dash = self.gridline_style self._plot.ygrid.grid_line_dash = self.gridline_style def set_tick_marks(self): """Set custom locations for the tick marks. """ from bokeh.models import FixedTicker if self.xticks: self._plot.xaxis[0].ticker = FixedTicker(ticks=self.xticks) if self.yticks: self._plot.yaxis[0].ticker = FixedTicker(ticks=self.yticks) if self.xticklabels_hide: self._plot.xaxis.major_label_text_font_size = '0pt' if self.yticklabels_hide: self._plot.yaxis.major_label_text_font_size = '0pt' def set_sources_heatmap(self): from bokeh.plotting import ColumnDataSource # initialize empty source if not hasattr(self, '_source'): self._source = ColumnDataSource(data=dict()) # remove mask from data -> not necessary soon? / convert to nan? var = np.ma.getdata(self._heatmap_var) self._source.add([var], 'image') self._source.add([self._data_xmin], 'x') self._source.add([self._data_ymin], 'y') self._source.add([self._data_xmax - self._data_xmin], 'dw') self._source.add([self._data_ymax - self._data_ymin], 'dh') def set_sources(self): """Set the source dictionaries to be used by the plotter functions. This is seperate to allow interactive updates of the data only. """ from bokeh.plotting import ColumnDataSource # check if heatmap if hasattr(self, '_heatmap_var'): return self.set_sources_heatmap() # 'copy' the zlabels iterator into src_zlbs self._zlbls, src_zlbs = itertools.tee(self._zlbls) # Initialise with empty dicts if not hasattr(self, "_sources"): self._sources = [ColumnDataSource(dict()) for _ in range(len(self._z_vals))] # range through all data and update the sources for i, (zlabel, data) in enumerate(zip(src_zlbs, self._gen_xy())): self._sources[i].add(data['x'], 'x') self._sources[i].add(data['y'], 'y') self._sources[i].add([zlabel] * len(data['x']), 'z_coo') # check for color for scatter plot if 'c' in data: self._sources[i].add(data['c'], 'c') # check if should set y_err as well if 'ye' in data: y_err_p = data['y'] + data['ye'] y_err_m = data['y'] - data['ye'] self._sources[i].add( list(zip(data['x'], data['x'])), 'y_err_xs') self._sources[i].add(list(zip(y_err_p, y_err_m)), 'y_err_ys') # check if should set x_err as well if 'xe' in data: x_err_p = data['x'] + data['xe'] x_err_m = data['x'] - data['xe'] self._sources[i].add( list(zip(data['y'], data['y'])), 'x_err_ys') self._sources[i].add(list(zip(x_err_p, x_err_m)), 'x_err_xs') def plot_legend(self, legend_items=None): """Add a legend to the plot. """ if self._use_legend: from bokeh.models import Legend loc = {'best': 'top_left'}.get(self.legend_loc, self.legend_loc) where = {None: 'right'}.get(self.legend_where, self.legend_where) # might be manually specified, e.g. from multiplot if legend_items is None: legend_items = self._lgnd_items lg = Legend(items=legend_items) lg.location = loc lg.click_policy = 'hide' self._plot.add_layout(lg, where) # Don't repeatedly redraw legend self._use_legend = False def set_mappable(self): from bokeh.models import LogColorMapper, LinearColorMapper import matplotlib as plt mappr_fn = (LogColorMapper if self.colormap_log else LinearColorMapper) bokehpalette = [plt.colors.rgb2hex(m) for m in self.cmap(range(256))] self.mappable = mappr_fn(palette=bokehpalette, low=self._zmin, high=self._zmax) def plot_colorbar(self): if self._use_colorbar: where = {None: 'right'}.get(self.legend_where, self.legend_where) from bokeh.models import ColorBar, LogTicker, BasicTicker ticker = LogTicker if self.colormap_log else BasicTicker color_bar = ColorBar(color_mapper=self.mappable, location=(0, 0), ticker=ticker(desired_num_ticks=6), title=self._ctitle) self._plot.add_layout(color_bar, where) def set_tools(self): """Set which tools appear for the plot. """ from bokeh.models import HoverTool self._plot.add_tools(HoverTool(tooltips=[ ("({}, {})".format(self.x_coo, self.y_coo if isinstance(self.y_coo, str) else None), "(@x, @y)"), (self.z_coo, "@z_coo")])) def update(self): from bokeh.io import push_notebook self.set_sources() push_notebook() def show(self, **kwargs): """Show the produced figure. """ if self.return_fig: return self._plot bshow(self._plot, **kwargs) return self def prepare_plot(self): self.prepare_axes() self.set_axes_labels() self.set_axes_range() self.set_spans() self.set_gridlines() self.set_tick_marks() self.set_sources()
def _prepare_data(connections, stations, lines, data, radius_feature, color_feature, features): """ Prepares data for plotting, creates objects necessary for bokeh. Parameters: ----------- connections, stations, lines - data from metro class data : pd.DataFrame() data prepared by metro class radius_feature : str feature name to use as radius scaling color_feature : str feature name to use as color for nodes features : list of strings list of column names to show on graph Returns: ------- source : ColumnDataSource with data TOOLTIPS : Tooltips for plot graph : networkx graph locations : dictionary of stations and normalized coordinates """ # create graph stations['node_name'] = stations['name'] + '_' + stations['line'].astype( str) connections['time'] = 1 graph = nx.Graph() for connection_id, connection in connections.iterrows(): ind1 = connection['station1'] - 1 ind2 = connection['station2'] - 1 station1_name = stations.iloc[ind1]['node_name'] station2_name = stations.iloc[ind2]['node_name'] graph.add_edge(station1_name, station2_name, time=connection['time']) normed = stations[['longitude', 'latitude']] normed = normed - normed.min() normed = normed / normed.max() locations = dict( zip(stations['node_name'], normed[['longitude', 'latitude']].values)) x = [] y = [] name = [] radius = [] fill_color = [] line_l = [] d = {i: [] for i in features} for node in graph.nodes(): # main values x.append(locations[node][0]) y.append(locations[node][1]) name.append(node.split('_')[0]) radius.append( np.clip( .01 * data.loc[(data['display_name'] == node.split('_')[0]) & (data['line'] == int(node.split('_')[1])), radius_feature].values[0], 0.003, 1)) colour = _pseudocolor( data.loc[(data['display_name'] == node.split('_')[0]) & (data['line'] == int(node.split('_')[1])), color_feature].values[0]) fill_color.append(_rgb2hex(tuple([int(np.round(i)) for i in colour]))) line_l.append(lines.loc[lines.line == int(node.split('_')[1]), 'name'].values[0]) # user defined values for k in d.keys(): d[k].append( str( np.round( data.loc[(data['display_name'] == node.split('_')[0]) & (data['line'] == int(node.split('_')[1])), 'total_traffic'].values[0], 2))) source = ColumnDataSource(data=dict(x=x, y=y, radius=radius, fill_color=fill_color, name=name, line_l=line_l)) for k, v in d.items(): source.add(v, k) TOOLTIPS = [] for i in features: TOOLTIPS.append((i, f'@{i}')) return source, TOOLTIPS, graph, locations
def stack_lines(datas, index_to_string, ylabel = "MW"): keys = sorted(datas.keys()) nb_max_values = max([ len(datas[key]) for key in keys]) indices = range(nb_max_values) #palette= brewer["Spectral"][nb_max_values] palette = bokeh.palettes.viridis(len(keys)) colors = {key : palette[i] for i,key in enumerate(keys)} xrange = [index_to_string(i) for i in range(nb_max_values+1)] lines = {} columns = {} for key in keys : columns[key] = [ datas[key][i] for i in indices ] ds = ColumnDataSource(columns) ds.add([index_to_string(i) for i in range(nb_max_values)], "left") ds.add([index_to_string(i+1) for i in range(nb_max_values)], "right") p=figure(x_range = xrange, height = defaults.height, width = defaults.width ) #print(ds.data) partial_sum = [ 0. for i in range(nb_max_values)] legend_items={} for key in keys: ds.add( partial_sum , "bottom"+str(key)) ds.add([ partial_sum[i] + datas[key][i] for i in range(nb_max_values)], "top"+str(key)) quad = p.quad( top = "top"+str(key) , bottom = "bottom"+str(key), left = "left", right = "right", color = colors[key], source = ds, fill_alpha = 0.7, hover_fill_alpha = 0.99, hover_fill_color=colors[key], hover_line_color=colors[key], ) legend_items[key] = [quad] partial_sum = ds.data["top"+str(key)] legend = Legend(items=[ (key, legend_items[key]) for key in legend_items.keys() ], location=(0, 0), orientation="horizontal") p.add_layout(legend, "below") p.yaxis.axis_label = ylabel #if there is two many lines, we do not put the hover tool if len(keys) > 10: return p hover = HoverTool( tooltips= [("time", "@left")] + [ (key, "@"+key+ " "+ylabel) for key in keys] , attachment="horizontal" , show_arrow=True ) p.add_tools(hover) return p
def plot_lines(datas_, index_to_string, ylabel = "MW"): datas = { str(key).replace(" ", "_") : value for key, value in datas_.items()} keys = sorted(datas.keys()) nb_max_values = max([ len(datas[key]) for key in keys]) indices = range(nb_max_values) palette = bokeh.palettes.viridis(len(keys)) colors = {key : palette[i] for i,key in enumerate(keys)} xrange = [index_to_string(i) for i in range(nb_max_values+1)] columns = {} for key in keys : columns[key] = [ datas[key][i] for i in indices ] lines = {} lines["time"] = [] for i in indices: lines["time"]+= [index_to_string(i), index_to_string(i+1)] for key in keys: lines[key] = [] for i in indices : v = datas[key][i] lines[key] += [v, v] ds = ColumnDataSource(lines) #print(xrange) p=figure(x_range = xrange , height = defaults.height, width = defaults.width ) legend_items={} lines = [] for key in keys: line = p.line( "time", key, color = colors[key], source = ds, line_width = 4, alpha = 0.7 ) legend_items[key] = [line] lines += [line] legend = Legend( items=[ (key, legend_items[key]) for key in sorted(legend_items.keys()) ] , location=(0, 0) , orientation="horizontal" ) p.add_layout(legend, "below") p.yaxis.axis_label = ylabel #if there is two many lines, we do not put the hover tool if len(keys) > 20: return p columns = {} for key in keys : columns[key] = [ datas[key][i] for i in indices ] hover_ds = ColumnDataSource(columns) hover_ds.add([index_to_string(i) for i in range(nb_max_values)], "time") hover_ds.add([index_to_string(i) for i in range(nb_max_values)], "left") hover_ds.add([index_to_string(i+1) for i in range(nb_max_values)], "right") max_val = -math.inf min_val = math.inf for key in keys : for i in indices : v = datas[key][i] if math.isfinite(v): max_val = max(max_val, v) min_val = min(min_val, v) hover_ds.add([max_val for i in range(nb_max_values)], "top") hover_ds.add([min_val for i in range(nb_max_values)], "bottom") hover_glyphs = [] #for key in keys: glyph = p.quad(top="top", bottom="bottom", left="left", right="right", alpha = 0, source = hover_ds, color = "white", hover_alpha=0.1, hover_color = "black") hover_glyphs += [glyph] hover = HoverTool( tooltips = [("time", "@time")] + [ (key, "@"+key + " " + ylabel ) for key in keys ] , attachment="horizontal" #, mode = "vline" , renderers = hover_glyphs ) p.add_tools(hover) return p
def commonPlot(r, ldict, y1lim, dset, height=None, width=None, y2=None): """ """ tools = "pan, wheel_zoom, box_zoom, crosshair, reset, save" title = ldict['title'] xlabel = ldict['xlabel'] y1label = ldict['y1label'] p = figure(title=title, x_axis_type='datetime', x_axis_label=xlabel, y_axis_label=y1label, tools=tools, output_backend="webgl") if height is not None: p.plot_height = height if width is not None: p.plot_width = width if y2 is not None: p.extra_y_ranges = y2 p.add_layout( LinearAxis(y_range_name="y2", axis_label=ldict['y2label']), 'right') # Annoyingly, the main y-axis still autoscales if there is a # second y-axis. Setting these means that the axis WON'T # autoscale until they're set back to None p.y_range = DataRange1d(start=y1lim[0], end=y1lim[1]) p.x_range.follow = "end" p.x_range.range_padding = 0.1 p.x_range.range_padding_units = 'percent' # Hack! But it works. Need to do this *before* you create cds below! # Includes a special flag (first=True) to pad the beginning so all # the columns in the final ColumnDataSource are the same length pix, piy = makePatches(r.index, y1lim, first=True) # The "master" data source to be used for plotting. # Generate it via the column names in the now-merged 'r' DataFrame # Start with the 'index' 'pix' and 'piy' since they're always those names mds = dict(index=r.index, pix=pix, piy=piy) # Start our plot source cds = ColumnDataSource(mds) # Now loop over the rest of our columns to fill it out, plotting as we go cols = r.columns lineSet = [] legendItems = [] for i, col in enumerate(cols): # Add our data to the cds cds.add(getattr(r, col), name=col) # Make the actual line plot object # TODO: Make this search in a given "y2" axis list if col.lower() == "humidity": lineObj, _ = plotLineWithPoints(p, cds, col, dset[i], yrname="y2") else: lineObj, _ = plotLineWithPoints(p, cds, col, dset[i]) lineSet.append(lineObj) # Now make it's corresponding legend item legendObj = LegendItem(label=col, renderers=[lineObj]) legendItems.append(legendObj) legend = Legend(items=legendItems, location="bottom_center", orientation='horizontal', spacing=15) p.add_layout(legend, 'below') # Customize the active tools p.toolbar.autohide = True # HACK HACK HACK HACK HACK # Apply the patches to carry the tooltips # # Shouldn't I just stream this instead of pix/nix and piy/niy ??? # simg = p.patches('pix', 'piy', source=cds, fill_color=None, fill_alpha=0.0, line_color=None) # This will also create the tooltips for each of the entries in cols ht = createHoverTool(simg, cols) p.add_tools(ht) return p, cds, cols