def _postprocess_hover(self, renderer, source): super(RasterPlot, self)._postprocess_hover(renderer, source) hover = self.handles.get('hover') if not (hover and isinstance(hover.tooltips, list)): return element = self.current_frame xdim, ydim = [dimension_sanitizer(kd.name) for kd in element.kdims] xaxis = self.handles['xaxis'] yaxis = self.handles['yaxis'] code = """ var {ax} = special_vars.{ax}; var date = new Date({ax}); return date.toISOString().slice(0, 19).replace('T', ' ') """ tooltips, formatters = [], dict(hover.formatters) for (name, formatter) in hover.tooltips: if isinstance(xaxis, DatetimeAxis) and formatter == '$x': xhover = CustomJSHover(code=code.format(ax='x')) formatters['$x'] = xhover formatter += '{custom}' if isinstance(yaxis, DatetimeAxis) and formatter == '$y': yhover = CustomJSHover(code=code.format(ax='y')) formatters['$y'] = yhover formatter += '{custom}' tooltips.append((name, formatter)) hover.tooltips = tooltips hover.formatters = formatters
def _postprocess_hover(self, renderer, source): super(GeoPlot, self)._postprocess_hover(renderer, source) hover = self.handles.get('hover') try: from bokeh.models import CustomJSHover except: CustomJSHover = None if (not self.geographic or None in (hover, CustomJSHover) or isinstance(hover.tooltips, basestring)): return element = self.current_frame xdim, ydim = [dimension_sanitizer(kd.name) for kd in element.kdims] code = """ var projections = require("core/util/projections"); var x = special_vars.data_x var y = special_vars.data_y var coords = projections.wgs84_mercator.inverse([x, y]) return "" + (coords[%d]).toFixed(4) """ formatters = { xdim: CustomJSHover(formatter=code % 0), ydim: CustomJSHover(formatter=code % 1), } tooltips = [] for name, formatter in hover.tooltips: if formatter in ('@{%s}' % xdim, '@{%s}' % ydim): formatter += '{custom}' tooltips.append((name, formatter)) hover.tooltips = tooltips hover.formatters = formatters
def _postprocess_hover(self, renderer, source): super(GeoPlot, self)._postprocess_hover(renderer, source) hover = self.handles.get('hover') try: from bokeh.models import CustomJSHover except: CustomJSHover = None if (not self.geographic or None in (hover, CustomJSHover) or isinstance(hover.tooltips, basestring) or self.projection is not GOOGLE_MERCATOR or hover.tooltips is None): return element = self.current_frame xdim, ydim = [dimension_sanitizer(kd.name) for kd in element.kdims] formatters, tooltips = {}, [] xhover = CustomJSHover(code=self._hover_code % 0) yhover = CustomJSHover(code=self._hover_code % 1) for name, formatter in hover.tooltips: customjs = None if formatter in ('@{%s}' % xdim, '$x'): dim = xdim customjs = xhover elif formatter in ('@{%s}' % ydim, '$y'): dim = ydim customjs = yhover if customjs: key = formatter if formatter in ('$x', '$y') else dim formatters[key] = customjs formatter += '{custom}' tooltips.append((name, formatter)) hover.tooltips = tooltips hover.formatters = formatters
def _set_tools(self): wheel_zoom = WheelZoomTool() pan = PanTool() box_zoom = BoxZoomTool() box_select = BoxSelectTool() crosshair = CrosshairTool() tap = TapTool() save = SaveTool() lasso_select = LassoSelectTool( select_every_mousemove=False, # enhance performance ) code = """ var projections = require("core/util/projections"); var x = special_vars.x var y = special_vars.y var coords = projections.wgs84_mercator.inverse([x, y]) return coords[%d].toFixed(2) """ tooltips = ''' <style> .bk-tooltip>div:not(:nth-child(-n+5)) {{ display:none; }} .bk-tooltip>div {{ background-color: #dff0d8; padding: 5px; }} </style> <b>STATION: </b> @{STNNBR} <br /> <b>LON: </b> @X_WMTS{custom} <br /> <b>LAT: </b> @Y_WMTS{custom} <br /> ''' hover = HoverTool(toggleable=True, mode='mouse', tooltips=tooltips, renderers=[self.env.wmts_map_scatter], formatters={ 'X_WMTS': CustomJSHover(code=code % 0), 'Y_WMTS': CustomJSHover(code=code % 1), }) tools = (pan, box_zoom, lasso_select, hover, crosshair, tap, wheel_zoom) self.env.wmts_map.add_tools(*tools) # set defaults self.env.wmts_map.toolbar.active_drag = pan self.env.wmts_map.toolbar.active_inspect = [crosshair, hover] self.env.wmts_map.toolbar.active_scroll = wheel_zoom self.env.wmts_map.toolbar.active_tap = None
def figure(df=None, **kwargs) -> Figure: if df is None: # just have at least some defaults.. dtypes = { 'date': 'datetime64', 'error': str, } else: dtypes = df.reset_index().dtypes.to_dict() tooltips = [] formatters = {} for c, t in dtypes.items(): fmt = None tfmt = '@' + c if df is None: dateish = 'datetime64' in str(t) else: # this is more reliable, works if there is a mix of timestamps.. s = df.reset_index()[c].dropna() dateish = len(s) > 0 and s.apply( lambda x: isinstance(x, (pd.Timestamp, datetime))).all() # TODO add to tests? if dateish: fmt = 'datetime' # todo %T only if it's actually datetime, not date? not sure though... tfmt += '{%F %a %T}' elif 'timedelta64' in str(t): # also meh fmt = CustomJSHover(code='const tick = value; ' + hhmm_formatter(unit=t)) tfmt += '{custom}' # eh, I suppose ok for now. would be nice to reuse in the tables... elif c == 'error': # FIXME ugh. safe here is potentially dangerous... need to figure out how to do this tfmt = '<pre>@error{safe}</pre>' if fmt is not None: formatters['@' + c] = fmt tooltips.append((c, tfmt)) # TODO very annoying, seems that if one of the tooltips is broken, nothing works at all?? need defensive tooltips.. # see https://docs.bokeh.org/en/latest/docs/user_guide/tools.html#formatting-tooltip-fields # TODO: use html tooltip with templating # and https://docs.bokeh.org/en/latest/docs/reference/models/formatters.html#bokeh.models.formatters.DatetimeTickFormatter from bokeh.models import HoverTool hover = HoverTool( tooltips=tooltips, formatters=formatters, # display a tooltip whenever the cursor is vertically in line with a glyph # TODO not sure I like this, it's a bit spammy mode='vline') from bokeh.plotting import figure as F # todo no need to pass plot_width? kw = {'plot_width': 2000} kw.update(**kwargs) f = F(**kw) # ugh. would be nice if add_tools returned self.. f.add_tools(hover) return f
def create_callbacks(self): subset = CustomJS(args=dict(source=self.sources['subset']), code=""" // get data source from Callback args var data = source.data; /// get BoxSelectTool dimensions from cb_data parameter of Callback var geometry = cb_data['geometry']; var x0 = geometry['x0']; var y0 = geometry['y0']; var x1 = geometry['x1']; var y1 = geometry['y1']; var xxs = [[[x0, x0, x1, x1]]]; var yys = [[[y0, y1, y1, y0]]]; /// update data source with new Rect attributes data['xs'].pop(); data['ys'].pop(); data['xs'].push(xxs); data['ys'].push(yys); // emit update of data source source.change.emit(); """) latitude = CustomJSHover(code=""" var projections = require("core/util/projections"); var x = special_vars.x var y = special_vars.y var coords = projections.wgs84_mercator.inverse([x, y]) return "" + coords[1].toFixed(6) """) longitude = CustomJSHover(code=""" var projections = require("core/util/projections"); var x = special_vars.x var y = special_vars.y var coords = projections.wgs84_mercator.inverse([x, y]) return "" + coords[0].toFixed(6) """) self.callbacks.update([('subset', subset), ('latitude', latitude), ('longitude', longitude)])
def cmatrix_plot_format(plot: Figure, format_cols: List, hover_code: str) -> Figure: plot.toolbar.logo = None plot.yaxis.axis_label_text_font_size = "16px" plot.yaxis.axis_label_text_color = '#003566' plot.xaxis.major_label_text_color = '#003566' plot.y_range.range_padding = 0 plot.hover.point_policy = "follow_mouse" plot.hover.attachment = "above" cust_formatters = {} for f in format_cols: code_ref = dashboard_constants.bucket_cust_format.format(field_name=f) if f == 'max_percentile_conf' else \ dashboard_constants.scale_format_code.format(field_name=f) cust_formatters[f] = CustomJSHover(args=dict(source=plot.renderers[0].data_source), code=code_ref) custom_hover_format = CustomJS(code=hover_code) format_dict = {f'@{c}': cust_formatters[c] for c in format_cols} plot.add_tools(HoverTool(tooltips=None, callback=custom_hover_format, formatters=format_dict)) someargs = dict(yaxis=plot.yaxis[0], xaxis=plot.xaxis[0], legend=plot.legend[0]) fig_resize_callback = CustomJS(args=someargs, code=dashboard_constants.fig_resize_callback_code) plot.js_on_change('inner_width', fig_resize_callback) return plot
def varstatistics(functionNode): logger = functionNode.get_logger() logger.info("==>>>> statistics " + functionNode.get_browse_path()) progressNode = functionNode.get_child("control").get_child("progress") progressNode.set_value(0) #functionNode.get_child("control.signal").set_value(None) vars = functionNode.get_child("variable").get_targets() widget = functionNode.get_child("widget").get_target() bins = functionNode.get_child("bins").get_value() tags = functionNode.get_child("annotations").get_value() startTime = date2secs(widget.get_child("startTime").get_value()) endTime = date2secs(widget.get_child("endTime").get_value()) vars = {var.get_id(): {"node": var} for var in vars} #first 30% progress: prog = Progress(progressNode) progressNode.set_value(0.1) prog.set_offset(0.1) #prog.set_divisor() if tags: allAnnoNodes = widget.get_child( "hasAnnotation.annotations").get_leaves() allAnnos = [] prog.set_divisor(len(allAnnoNodes) / 0.2) for index, node in enumerate(allAnnoNodes): prog.set_progress(index) if node.get_child("type").get_value() == "time": thisTags = node.get_child("tags").get_value() if any(tag in tags for tag in thisTags): anno = {} for child in node.get_children(): anno[child.get_name()] = child.get_value() if date2secs(anno["startTime"]) >= startTime and date2secs( anno["endTime"] ) <= endTime: #take this anno only if it is inside the current start/end time allAnnos.append(anno) if allAnnos == []: give_up(functionNode, "no matching annotations in selected time") return False else: allAnnos = [] progressNode.set_value(0.3) logger.debug(f"statistics annotations to look at: {len(allAnnos)}") prog.set_offset(0.3) totalAnnos = max(len(allAnnos), 1) totalCount = len(vars) * totalAnnos prog.set_divisor(totalCount / 0.3) totalValids = 0 for varIndex, var in enumerate(vars): info = vars[var] if tags: #iterate over all start and end times values = numpy.asarray([], dtype=numpy.float64) for annoIndex, anno in enumerate(allAnnos): thisValues = info["node"].get_time_series( anno["startTime"], anno["endTime"])["values"] values = numpy.append(values, thisValues) myCount = varIndex * totalAnnos + annoIndex prog.set_progress(myCount) else: values = info["node"].get_time_series(startTime, endTime)["values"] valids = numpy.count_nonzero(~numpy.isfinite(values)) totalValids += valids hist, edges = numpy.histogram(values, bins=bins) hist = hist / len(values) #normalize info["hist"] = hist info["edges"] = edges #make a plot if totalValids == 0: give_up( functionNode, "all Variables are have no data in the time and annotations selected" ) return False progressNode.set_value(0.6) hover1 = HoverTool(tooltips=[('x,y', '$x,$y')], mode='mouse') hover1.point_policy = 'snap_to_data' hover1.line_policy = "nearest" tools = [ PanTool(), WheelZoomTool(), BoxZoomTool(), ResetTool(), SaveTool(), hover1 ] title = "Statistics of " + str( [info["node"].get_name() for var, info in vars.items()]) if tags: title = title + " in annotation: " + str(tags) fig = figure(title=title, tools=tools, plot_height=300) fig.toolbar.logo = None curdoc().theme = Theme(json=themes.darkTheme) fig.xaxis.major_label_text_color = themes.darkTickColor fig.yaxis.major_label_text_color = themes.darkTickColor for index, var in enumerate(vars): info = vars[var] col = themes.darkLineColors[index] hist = info["hist"] edges = info["edges"] fig.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:], fill_color=col, line_color=col, alpha=0.8, legend_label=info["node"].get_name()) fig.legend.location = "top_left" fileName = functionNode.get_child("fileName").get_value() filePath = os.path.join(myDir, './../web/customui/' + fileName) # now make the trend box plot, but only for tags # for each variable we create statistics for the annotations and prepare the data # {"node":Node(), "boxLower":[], "boxUpper", "mean", "limitUpper", "limitLower"} # startTime = date2secs(widget.get_child("startTime").get_value( )) #we only take tags that are inside the current zoom of the widgets endTime = date2secs(widget.get_child("endTime").get_value()) boxPlots = [] allTimes = [] if tags: for index, var in enumerate(vars): info = { "node": vars[var]["node"], "boxLower": [], "boxUpper": [], "median": [], "time": [], "limitUpper": [], "limitLower": [], "mean": [] } for anno in allAnnos: data = info["node"].get_time_series(anno["startTime"], anno["endTime"]) if len(data["values"]): data["values"] = data["values"][numpy.isfinite( data["values"])] #remove the nan if len(data["values"]): #make the statistics info["time"].append(numpy.median(data["__time"]) * 1000) allTimes.append(numpy.median(data["__time"]) * 1000) info["limitLower"].append( numpy.quantile(data["values"], 0.01)) info["limitUpper"].append( numpy.quantile(data["values"], 0.99)) info["boxLower"].append( numpy.quantile(data["values"], 0.25)) info["boxUpper"].append( numpy.quantile(data["values"], 0.75)) info["median"].append(numpy.median(data["values"])) info["mean"].append(numpy.mean(data["values"])) boxPlots.append(info) format = "%Y-%m-%d-T%H:%M:%S" custom = """var local = moment(value).tz('UTC'); return local.format();""" #%self.server.get_settings()["timeZone"] hover = HoverTool(tooltips=[('date', '@x{%F}')], formatters={'@x': CustomJSHover(code=custom)}, mode='mouse') hover.point_policy = 'snap_to_data' hover.line_policy = "nearest" tools = [ PanTool(), BoxZoomTool(), WheelZoomTool(), ResetTool(), hover, SaveTool() ] fig2 = figure(title="trends", tools=tools, plot_height=300, x_axis_type='datetime') fig2.xaxis.major_label_text_color = themes.darkTickColor fig2.yaxis.major_label_text_color = themes.darkTickColor progressNode.set_value(0.7) fig2.xaxis.formatter = DatetimeTickFormatter(years=format, days=format, months=format, hours=format, hourmin=format, minutes=format, minsec=format, seconds=format) fig2.toolbar.logo = None #fig2.line([1,2,3],[1,2,3]) #calc with of vbars if len(allAnnos) > 1: xTimesStart = min(allTimes) xTimesEnd = max(allTimes) width = (xTimesEnd - xTimesStart) / 2 / len(allAnnos) else: width = 1000000 for index, info in enumerate(boxPlots): #each info is for one variable col = themes.darkLineColors[index] fig2.segment(info["time"], info["limitUpper"], info["time"], info["boxUpper"], line_color=col) fig2.segment(info["time"], info["limitLower"], info["time"], info["boxLower"], line_color=col) width = 20 #fig2.vbar(info["time"],width=width,bottom=info["median"],top=info["boxUpper"],fill_color=col,line_color="black",width_units='screen') #fig2.vbar(info["time"],width=width,bottom=info["boxLower"],top=info["median"],fill_color=col,line_color="black",width_units='screen') #upper box sizUpper = numpy.asarray(info["boxUpper"]) - numpy.asarray( info["median"]) medUpper = numpy.asarray(info["median"]) + sizUpper / 2 fig2.rect(x=info["time"], y=medUpper, width_units='screen', width=20, height=sizUpper, fill_color=col, line_color="black") #lower box sizLower = numpy.asarray(info["median"]) - numpy.asarray( info["boxLower"]) medLower = numpy.asarray(info["median"]) - sizLower / 2 fig2.rect(x=info["time"], y=medLower, width_units='screen', width=20, height=sizLower, fill_color=col, line_color="black") #sort data for line x = numpy.asarray(info["time"]) y = numpy.asarray(info["mean"]) order = numpy.argsort(x) x = x[order] y = y[order] fig2.line(x, y, line_color=col) progressNode.set_value(0.8) else: #no fig2 pass output_file( filePath, mode="inline" ) #inline: put the bokeh .js into this html, otherwise the default cdn will be taken, might cause CORS problems if tags: save(layout([[fig], [fig2]])) else: save(fig) return True
output_file("customjs_hover.html") # range bounds supplied in web mercator coordinates p = figure(x_range=(-2000000, 6000000), y_range=(-1000000, 7000000), x_axis_type="mercator", y_axis_type="mercator") p.add_tile(CARTODBPOSITRON) p.circle(x=[0, 2000000, 4000000], y=[4000000, 2000000, 0], size=30) code = """ var projections = require("core/util/projections"); var x = special_vars.x var y = special_vars.y var coords = projections.wgs84_mercator.inverse([x, y]) return coords[%d].toFixed(2) """ p.add_tools( HoverTool(tooltips=[ ('lon', '@x{custom}'), ('lat', '@y{custom}'), ], formatters={ 'x': CustomJSHover(code=code % 0), 'y': CustomJSHover(code=code % 1), })) show(p)
<p style="font-size:1.1em; font-weight:bold; margin:0px; padding:0px;">@start_name </br> <span style="color:#488ebc; font-size:1.1em; font-weight:bold">@end_name</span></br> <span style="font-size:0.9em; white-space:nowrap;">Expr_value: @value</span> </p> </div> <div style="display:@display{custom};"> <p style="font-size:1.1em; font-weight:bold; margin:0px; padding:0px;"> <span style="color:#488ebc; font-size:1.1em; font-weight:bold; white-space:nowrap;"> @start_name ---- @end_name </span></br> </p> </div> ''' ## Display disparate tooltip contents for cells-genes edges and L-R pairs edges cus_tt = CustomJSHover(code=''' if (value==" ") {return "none"} else if (value=="none") {return " "} ''') hover = HoverTool(renderers=[graphA], tooltips=TOOLTIPS, show_arrow=False, formatters=dict(display=cus_tt)) plot.add_tools(hover, TapTool()) graphA.selection_policy = NodesAndLinkedEdges() graphA.inspection_policy = EdgesAndLinkedNodes() plot.renderers.append(graphA) labels = LabelSet(x='x', y='y', text='name', text_color='#00cadb',
]) return my_stats custom = CustomJSHover(code=""" if (value==0) { return "" } var modified; var SI_SYMBOL = ["", "k", "m", "b", "t"]; modified = 100000/value; // what tier? (determines SI symbol) var tier = Math.log10(modified) / 3 | 0; // if zero, we don't need a suffix if(tier == 0) return "(1/" + modified.toFixed(0) + " Ppl)"; // get suffix and determine scale var suffix = SI_SYMBOL[tier]; var scale = Math.pow(10, tier * 3); // scale the number var scaled = modified / scale; // format number and add suffix return "(1/" + scaled.toFixed(1) + suffix + " Ppl)"; """) def my_format(num): if num == 0:
def __init__(self, image_views, sv_metadata, sv_streamctrl, positions=POSITIONS): """Initialize a resolution rings overlay. Args: image_views (ImageView): Associated streamvis image view instances. sv_metadata (MetadataHandler): A metadata handler to report metadata issues. sv_streamctrl (StreamControl): A StreamControl instance of an application. positions (list, optional): Scattering radii in Angstroms. Defaults to [1.4, 1.5, 1.6, 1.8, 2, 2.2, 2.6, 3, 5, 10]. """ self._sv_metadata = sv_metadata self._sv_streamctrl = sv_streamctrl self.positions = np.array(positions) # ---- add resolution tooltip to hover tool self._formatter_source = ColumnDataSource(data=dict( detector_distance=[np.nan], beam_energy=[np.nan], beam_center_x=[np.nan], beam_center_y=[np.nan], )) resolution_formatter = CustomJSHover( args=dict(params=self._formatter_source), code=js_resolution) hovertool_off = HoverTool(tooltips=[("intensity", "@image")], names=["image_glyph"]) hovertool_on = HoverTool( tooltips=[("intensity", "@image"), ("resolution", "@x{resolution} Å")], formatters={"@x": resolution_formatter}, names=["image_glyph"], ) # ---- resolution rings self._source = ColumnDataSource( dict(x=[], y=[], w=[], h=[], text_x=[], text_y=[], text=[])) ellipse_glyph = Ellipse(x="x", y="y", width="w", height="h", fill_alpha=0, line_color="white") text_glyph = Text( x="text_x", y="text_y", text="text", text_align="center", text_baseline="middle", text_color="white", ) cross_glyph = Cross(x="beam_center_x", y="beam_center_y", size=15, line_color="red") for image_view in image_views: image_view.plot.add_glyph(self._source, ellipse_glyph) image_view.plot.add_glyph(self._source, text_glyph) image_view.plot.add_glyph(self._formatter_source, cross_glyph) # ---- toggle button def toggle_callback(state): hovertool = hovertool_on if state else hovertool_off for image_view in image_views: image_view.plot.tools[-1] = hovertool toggle = CheckboxGroup(labels=["Resolution Rings"], default_size=145) toggle.on_click(toggle_callback) self.toggle = toggle
def orders_by_day(days): df = pd.read_csv("Levia_Live_Sales.csv") from bokeh.core.properties import value from bokeh.io import show from bokeh.models import ColumnDataSource, HoverTool from bokeh.plotting import figure from bokeh.io import show from bokeh.models import ColumnDataSource, HoverTool, CustomJSHover from bokeh.plotting import figure from bokeh.io import output_file, show from bokeh.palettes import Category20c from bokeh.plotting import figure from bokeh.transform import cumsum from bokeh.palettes import Turbo256, Turbo, inferno from bokeh.models import LabelSet, ColumnDataSource from bokeh.models import ColumnDataSource, HoverTool #df=pd.read_csv("LeviaExpenses.csv",index_col=0) output_file("stacked.html") dff = df[df["Date"].isin(days)] dfn = pd.DataFrame(index=days, columns=[act for act in pd.unique(dff["Account"])]) print(dff) for i, d in enumerate(dfn.index): for act in dfn: dfff = dff[dff["Account"] == act] dfff = dfff[dfff["Date"] == d].reset_index() try: dfn[act][i] = dfff["Total"][0] except: dfn[act][i] = 0 st.dataframe(dfn) data = {"Years": [x[:-5] for x in days]} for i, col in enumerate(dfn.columns): data[col] = dfn[col].to_list() print(data) #data['color'] = Category20c[len(dict)] colors = inferno(43) #st.write(colors) df = pd.DataFrame(data) df = df.set_index('Years').rename_axis(None) source = ColumnDataSource(data=df) colors = inferno(len(df.columns)) p = figure(x_range=[x for x in df.index], plot_height=500, plot_width=750, title="Orders by Day by Dispensary", toolbar_location=None, tools="") columns = [x for x in df.columns] renderers = p.vbar_stack(columns, x='index', width=0.9, color=colors, source=source, legend_label=columns, name=columns) formatter = CustomJSHover(args=dict(source=source), ) for r in renderers: hover = HoverTool(tooltips=[("Account", "$name"), ("Day", "@index"), ('Cases', "@$name{0}")], formatters={'@%s' % r.name: formatter}, renderers=[r]) p.add_tools(hover) # this doesn't trigger the formatter p.y_range.start = 0 p.x_range.range_padding = 0.1 p.xgrid.grid_line_color = None p.axis.minor_tick_line_color = None p.outline_line_color = None p.legend.location = "top_left" p.legend.orientation = "horizontal" st.bokeh_chart(p)
def orders_by_acct(days): df = pd.read_csv("Levia_Live_Sales.csv") from bokeh.models import ColumnDataSource, HoverTool, CustomJSHover from bokeh.io import output_file, show from bokeh.plotting import figure from bokeh.palettes import inferno from bokeh.models import ColumnDataSource, HoverTool output_file("stacked.html") dff = df[df["Date"].isin(days)].sort_values(by="Total") dfn = pd.DataFrame(index=[act for act in pd.unique(dff["Account"])], columns=["Cel", "Ach", "Dre"]) print(dff) for i, act in enumerate(dfn.index): for flv in dfn: dfff = dff[dff["Account"] == act] try: dfn[flv][i] = sum(dfff[flv].to_list()) except: dfn[flv][i] = 0 st.dataframe(dfn) #dfn=dfn.sort_values(by="") data = {"Years": [x for x in dfn.index]} for i, col in enumerate(dfn.columns): data[col] = dfn[col].to_list() print(data) #data['color'] = Category20c[len(dict)] colors = inferno(43) #st.write(colors) df = pd.DataFrame(data) df = df.set_index('Years').rename_axis(None) source = ColumnDataSource(data=df) colors = inferno(len(df.columns)) p = figure(x_range=[x for x in df.index], plot_height=500, plot_width=750, title="Orders By Account", toolbar_location=None, tools="") columns = [x for x in df.columns] p.xaxis.major_label_orientation = "vertical" renderers = p.vbar_stack(columns, x='index', width=0.9, color=colors, source=source, legend_label=columns, name=columns) formatter = CustomJSHover(args=dict(source=source), ) for r in renderers: hover = HoverTool(tooltips=[("Flavor", "$name"), ("Account", "@index"), ('Cases', "@$name{0}")], formatters={'@%s' % r.name: formatter}, renderers=[r]) p.add_tools(hover) # this doesn't trigger the formatter p.y_range.start = 0 p.x_range.range_padding = 0.1 p.xgrid.grid_line_color = None p.axis.minor_tick_line_color = None p.outline_line_color = None p.legend.location = "top_left" p.legend.orientation = "horizontal" st.bokeh_chart(p) pass
output_file("customjs_hover.html") # range bounds supplied in web mercator coordinates p = figure(x_range=(-2000000, 6000000), y_range=(-1000000, 7000000), x_axis_type="mercator", y_axis_type="mercator") p.add_tile(get_provider(CARTODBPOSITRON)) p.circle(x=[0, 2000000, 4000000], y=[4000000, 2000000, 0], size=30) code = """ var projections = Bokeh.require("core/util/projections"); var x = special_vars.x var y = special_vars.y var coords = projections.wgs84_mercator.inverse([x, y]) return coords[%d].toFixed(2) """ p.add_tools( HoverTool(tooltips=[ ('lon', '$x{custom}'), ('lat', '$y{custom}'), ], formatters={ '$x': CustomJSHover(code=code % 0), '$y': CustomJSHover(code=code % 1), })) show(p)
def make_cluster_plot(plot_data, pod, recs): # get x, y coordinates for pod and recs main_pod = pod.merge(plot_data, on=["title", "itunes_id", "genre", "subgenre"]) recs = recs.merge(plot_data, on=["title", "itunes_id", "genre", "subgenre"]) genre_list = list(plot_data.groupby(by="genre").groups.keys()) show_list = recs["genre"].unique() p = figure(tools="wheel_zoom,pan,box_zoom,reset,tap",\ plot_width=700, plot_height=500,\ toolbar_location="right") p.toolbar.active_drag = None # disabling pan helps with scrolling on smartphones # plot all data points and group them by genres for the legends group = dict() legend_items = [] for i, g in enumerate(genre_list): source = ColumnDataSource(plot_data.loc[plot_data["genre"] == g]) group[g] = p.circle(x='x', y='y', size = 10,\ color = Category20[19][-(i+1)],\ fill_alpha=0.5,\ line_alpha=0, muted_color="lightgray",\ source = source, muted_alpha = 0.05) legend_items.append((g,[group[g]])) if g not in show_list: # only show genres that are in the recommendations group[g].muted = True # plot the main podcast p.image_url(url="artwork_url", x="x", y="y", w=50, h = 50, \ anchor="center", global_alpha = 0.8, \ h_units = "screen", w_units = "screen",\ source = ColumnDataSource(main_pod)) p.rect(main_pod['x'], main_pod['y'], width = 52, height = 52, \ line_width = 3, height_units="screen", width_units="screen", \ fill_alpha = 0, line_color = "red") # plot the recommendations on the current page recs_source = ColumnDataSource(recs) rend_main = p.rect('x', 'y', width = 18, height = 18, \ line_width = 2, height_units="screen", width_units="screen", \ fill_alpha = 0, line_color = "royalblue", source = recs_source) p.image_url(url="artwork_url", x="x", y="y", w=16, h=16, \ anchor="center", h_units="screen", w_units="screen",\ source=recs_source) # plot the recommendations on the current page # rend_main = p.hex('x', 'y', fill_color = 'ivory', line_color = "royalblue", \ # line_width = 3, size = 15, \ # fill_alpha = 1, source = recs_source) # plot the main podcast # p.circle(main_pod['x'], main_pod['y'], color = "red", size = 5) # p.circle(main_pod['x'], main_pod['y'], fill_color = "red", \ # fill_alpha = 0.2, radius = 10, line_color = "red", \ # line_alpha = 0.2) custom_hover = HoverTool(mode="mouse", point_policy="snap_to_data", \ muted_policy = "ignore", renderers = [rend_main]) # limit the hover list to 5 items in case of overlapping glyphs custom_formatter = CustomJSHover(code=""" special_vars.indices = special_vars.indices.slice(0,5) if (special_vars.indices.indexOf(special_vars.index) >= 0) { return " " } else { return " hidden " }""") custom_hover.tooltips = """ <div @title{custom|safe}> <div> <img src="@artwork_url" alt="@artwork_url" width="42" style="margin: 5px 5px 5px 5px;" border="2" ></img> <span style="font-size: 15px; font-weight: bold;">@title</span> </div> </div> """ custom_hover.formatters = {'@title':custom_formatter} p.add_tools(custom_hover) p.outline_line_width = 7 p.outline_line_alpha = 0.2 p.outline_line_color = "navy" p.toolbar.autohide = True p.grid.visible = False legends = [] for i in range(0, len(legend_items), 4): legends.append(Legend(items=legend_items[i:i+4], margin=2)) for legend in legends: p.add_layout(legend,'below') p.plot_height = (600 + ((len(legend_items)-1)//4)*40) p.legend.click_policy="mute" p.legend.location = "bottom_center" p.legend.orientation = "horizontal" # url for selected recommendation selection_url_code = """ var ind = source.selected.indices if (ind.length==1) { var itunes_id = source.data['itunes_id'][ind] } else { var itunes_id = source.data['itunes_id'][ind[0]] } var url = "/itunes_id=".concat(itunes_id, "&offset=0") window.open(url, "_self") """ taptool = p.select(type=TapTool) taptool.renderers = [rend_main] taptool.callback = CustomJS(args = dict(source = recs_source), \ code = selection_url_code) # make the figure responsive clusters = column([p], sizing_mode = "stretch_width") return components(clusters)
# merge the data sets merged_df = pd.merge(arcgis_df, momo_df, left_index=True, right_index=True, how='outer') merged_df['expected_plus_covid'] = merged_df['defunciones_esperadas'] + merged_df['deaths_covid'] merged_df["datestring"] = merged_df.index.strftime("%b %d") # set up the figure p = figure(plot_width=1000, plot_height=400, tools='pan,wheel_zoom,reset', x_axis_type="datetime", toolbar_location=None, name='COVID - Canary Islands', x_range=DataRange1d(bounds="auto"), y_range=Range1d(0, (max(merged_df['defunciones_esperadas_q99']) + 25), bounds="auto")) p.sizing_mode = 'scale_width' # Set up hover tooltips covid_hover_formatter = CustomJSHover(code=""" if (isNaN(value)) { return "-" } return value.toString(); """) p.add_tools(HoverTool( tooltips=[("Date", '@datestring'), ("Expected Deaths", "@defunciones_esperadas"), ("Observed Deaths", "@defunciones_observadas"), ('COVID-19 Deaths', '@deaths_covid{custom}')], formatters={'@deaths_covid': covid_hover_formatter} )) # format x axis p.xaxis.formatter = DatetimeTickFormatter( hours=["%B %-d"], days=["%B %-d"],
class MyPlot: # figure p = None # palette palette = list(Colorblind8) # NaN hover formatter nan_hover_formatter = CustomJSHover(code=""" if (isNaN(value)) { return "-" } return value.toString(); """) def __init__(self, figure_name, x_lower_bound=None, x_upper_bound=None, y_lower_bound=None, y_upper_bound=None): # set up the figure self.p = figure(plot_width=1000, plot_height=400, tools='pan,wheel_zoom,reset', x_axis_type="datetime", toolbar_location=None, name=figure_name, x_range=DataRange1d(x_lower_bound, x_upper_bound, bounds="auto"), y_range=Range1d(y_lower_bound, y_upper_bound, bounds="auto")) self.p.sizing_mode = 'scale_width' # set up border self.p.border_fill_color = "whitesmoke" self.p.min_border_top = 15 # format x axis self.p.xaxis.formatter = DatetimeTickFormatter( hours=["%B %-d"], days=["%B %-d"], months=["%B %-d"], years=["%B %-d"], ) def add_line(self, x_column, y_column, source, name, legend): self.p.line(x=x_column, y=y_column, source=ColumnDataSource(source), name=name, legend_label=legend, color=self.get_next_colour(), line_width=1) def add_vertical_bar(self, date, label): # create and add span to figure # date should be created via datetime.datetime(2001,1,1) or equivalent span_date = date.timestamp() * 1000 vertical_span = Span(location=span_date, dimension='height', line_color=self.palette.pop(), line_dash='dashed', line_width=2) span_label = Label(x=span_date, y=10, y_units='screen', text=' ' + label, text_font='helvetica', text_font_size='9pt') self.p.add_layout(vertical_span) self.p.add_layout(span_label) def add_band(self, x_column, source, max_values_column, min_values_column): # add a 'confidence interval' or band to the figure max_min_interval = Band(base=x_column, lower=max_values_column, upper=min_values_column, source=ColumnDataSource(source), level='underlay', fill_alpha=1.0, line_width=1, line_color='black') self.p.add_layout(max_min_interval) def add_hover_tool(self, hover_tool): self.p.add_tools(hover_tool) def create_legend(self): # setup legend self.p.legend.location = "top_left" self.p.legend.click_policy = "hide" self.p.legend.label_text_font_size = "8pt" self.p.legend.spacing = 0 self.p.legend.border_line_color = None def create_readme_from_template(self): # create the legend (note this needs to be done last after all glyphs have been added) self.create_legend() # create the json data for the plot item_text = json.dumps(json_item(self.p, "Plot")) with open('../docs/page.template', 'r') as file: template = file.read() # Replace the target strings template = template.replace('{PLACEHOLDER}', item_text) now = datetime.now() template = template.replace('{DATE}', now.strftime("%d/%m/%Y %H:%M:%S")) # Write the file out again with open('../docs/README.md', 'w') as file: print("Writing new README.md.") file.write(template) def get_next_colour(self): return self.palette.pop()
def expense_graph(): from bokeh.core.properties import value from bokeh.io import show from bokeh.models import ColumnDataSource, HoverTool from bokeh.plotting import figure from bokeh.io import show from bokeh.models import ColumnDataSource, HoverTool, CustomJSHover from bokeh.plotting import figure from bokeh.models import ColumnDataSource, HoverTool df = pd.read_csv("LeviaExpenses.csv", index_col=0) output_file("stacked.html") data = {"Years": [x for x in df.index]} for i, col in enumerate(df.columns): data[col] = df[col].to_list() print(data) #data['color'] = Category20c[len(dict)] colors = inferno(43) #st.write(colors) df = pd.DataFrame(data) df = df.set_index('Years').rename_axis(None) source = ColumnDataSource(data=df) p = figure(x_range=[x for x in df.index], plot_height=750, plot_width=1900, title="Fruit Counts by Year", toolbar_location=None, tools="") columns = [x for x in df.columns] renderers = p.vbar_stack(columns, x='index', width=0.9, color=colors, source=source, legend_label=columns, name=columns) formatter = CustomJSHover(args=dict(source=source), ) for r in renderers: hover = HoverTool(tooltips=[("Expense Class", "$name"), ("Period", "@index"), ('$', "@$name{0.00}")], formatters={'@%s' % r.name: formatter}, renderers=[r]) p.add_tools(hover) # this doesn't trigger the formatter p.y_range.start = 0 p.x_range.range_padding = 0.1 p.xgrid.grid_line_color = None p.axis.minor_tick_line_color = None p.outline_line_color = None p.legend.location = "top_left" p.legend.orientation = "horizontal" st.bokeh_chart(p)
def stacked_viz( df: pd.DataFrame, x: str, y: str, grp_cnt_stats: Dict[str, int], plot_width: int, plot_height: int, timeunit: Optional[str] = None, max_lbl_len: int = 15, ) -> Panel: """ Render a stacked bar chart """ # pylint: disable=too-many-arguments,too-many-locals title = _make_title(grp_cnt_stats, x, y) if not timeunit: if grp_cnt_stats[f"{x}_shw"] > 30: plot_width = 32 * grp_cnt_stats[f"{x}_shw"] else: if len(df) > 30: plot_width = 32 * len(df) fig = figure( x_range=list(df.index), toolbar_location=None, title=title, tools=[], plot_width=plot_width, plot_height=plot_height, ) grps = list(df.columns) palette = Pastel1[9] * (len(grps) // len(Pastel1) + 1) if "Others" in grps: colours = palette[0:len(grps) - 1] + ("#636363", ) else: colours = palette[0:len(grps)] source = ColumnDataSource(data=df) renderers = fig.vbar_stack( stackers=grps, x="index", width=0.9, source=source, line_width=1, color=colours, ) grps = [ (grp[:(max_lbl_len - 1)] + "...") if len(grp) > max_lbl_len else grp for grp in grps ] legend_it = [(grp, [rend]) for grp, rend in zip(grps, renderers)] legend = Legend(items=legend_it) legend.label_text_font_size = "8pt" fig.add_layout(legend, "right") if not timeunit: tooltips = [("Group", "@index, $name"), ("Percentage", "@$name{0.2f}%")] fig.add_tools(HoverTool(tooltips=tooltips)) fig.yaxis.axis_label = "Percent" else: # below is for having percent and count in the tooltip formatter = CustomJSHover( args=dict(source=source), code=""" const columns = Object.keys(source.data) const cur_bar = special_vars.data_x - 0.5 var ttl_bar = 0 for (let i = 0; i < columns.length; i++) { if (columns[i] != 'index'){ ttl_bar = ttl_bar + source.data[columns[i]][cur_bar] } } const cur_val = source.data[special_vars.name][cur_bar] return (cur_val/ttl_bar * 100).toFixed(2)+'%'; """, ) for rend in renderers: hover = HoverTool( tooltips=[ (y, "$name"), (timeunit, "@index"), ("Count", "@$name"), ("Percent", "@{%s}{custom}" % rend.name), ], formatters={"@{%s}" % rend.name: formatter}, renderers=[rend], ) fig.add_tools(hover) fig.yaxis.axis_label = "Count" _format_axis(fig, 0, df.sum(axis=1).max(), "y") fig.xaxis.axis_label = x if timeunit == "Week of": fig.xaxis.axis_label = x + ", the week of" tweak_figure(fig, "stacked") return Panel(child=fig, title="stacked bar chart")
def render_bar_chart( data: Tuple[np.ndarray, np.ndarray, np.ndarray], yscale: str, plot_width: int, plot_height: int, ) -> Figure: """ Render a bar chart for the missing and present values """ pres_cnts, null_cnts, cols = data df = pd.DataFrame({"Present": pres_cnts, "Missing": null_cnts}, index=cols) if len(df) > 20: plot_width = 28 * len(df) fig = Figure( x_range=list(df.index), y_range=[0, df["Present"][0] + df["Missing"][0]], plot_width=plot_width, plot_height=plot_height, y_axis_type=yscale, toolbar_location=None, tools=[], title=" ", ) rend = fig.vbar_stack( stackers=df.columns, x="index", width=0.9, color=[CATEGORY20[0], CATEGORY20[2]], source=df, legend_label=list(df.columns), ) # hover tool with count and percent formatter = CustomJSHover( args=dict(source=ColumnDataSource(df)), code=""" const columns = Object.keys(source.data) const cur_bar = special_vars.data_x - 0.5 var ttl_bar = 0 for (let i = 0; i < columns.length; i++) { if (columns[i] != 'index'){ ttl_bar = ttl_bar + source.data[columns[i]][cur_bar] } } const cur_val = source.data[special_vars.name][cur_bar] return (cur_val/ttl_bar * 100).toFixed(2)+'%'; """, ) for i, val in enumerate(df.columns): hover = HoverTool( tooltips=[ ("Column", "@index"), (f"{val} count", "@$name"), (f"{val} percent", "@{%s}{custom}" % rend[i].name), ], formatters={"@{%s}" % rend[i].name: formatter}, renderers=[rend[i]], ) fig.add_tools(hover) fig.yaxis.axis_label = "Row Count" tweak_figure(fig) relocate_legend(fig, "left") fig.frame_width = plot_width return fig