Here are the examples of the python api bokeh.models.CustomJSHover taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.
5 Examples
0
View Source File : bokeh.py
License : MIT License
Project Creator : karlicoss
License : MIT License
Project Creator : karlicoss
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
# not sure if it's that useful.. for a single parameter.
def date_figure(df=None, **kwargs) -> Figure:
0
View Source File : render.py
License : MIT License
Project Creator : sfu-db
License : MIT License
Project Creator : sfu-db
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,
) -> Panel:
"""
Render a stacked bar chart
"""
# pylint: disable=too-many-arguments,too-many-locals
# percent
df2 = df.div(df.sum(axis=1), axis=0) * 100
df.columns = [f"{col}_cnt" for col in df.columns]
# final dataframe contains percent and count
df = pd.concat([df2, df], axis=1)
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(
plot_height=plot_height,
plot_width=plot_width,
title=title,
toolbar_location=None,
x_range=list(df.index),
)
grps = list(df2.columns)
palette = PASTEL1 * (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[:14] + "...") if len(grp) > 15 else grp for grp in grps]
legend = Legend(items=[(grp, [rend]) for grp, rend in zip(grps, renderers)])
legend.label_text_font_size = "8pt"
fig.add_layout(legend, "right")
if not timeunit:
# include percent and count in the tooltip
formatter = CustomJSHover(
args=dict(source=source),
code="""
const cur_bar = special_vars.data_x - 0.5
const name_cnt = special_vars.name + '_cnt'
return source.data[name_cnt][cur_bar] + '';
""",
)
for rend in renderers:
hover = HoverTool(
tooltips=[
(x, "@index"),
(y, "$name"),
("Percentage", "@$name%"),
("Count", "@{%s}{custom}" % rend.name),
],
formatters={"@{%s}" % rend.name: formatter},
renderers=[rend],
)
fig.add_tools(hover)
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 heatmap_viz(
0
View Source File : render.py
License : MIT License
Project Creator : sfu-db
License : MIT License
Project Creator : sfu-db
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
cols = [f"{col[:13]}...{col[len(col)-3:]}" if len(col) > 18 else col for col in cols]
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")
return fig
def render_missing_spectrum(
0
View Source File : widgets.py
License : MIT License
Project Creator : smartyal
License : MIT License
Project Creator : smartyal
def __make_tooltips(self, force=False):
#return
#make the hover tool
"""
if we create a hover tool, it only appears if we plot a line, we need to hook the hover tool to the figure and the toolbar separately:
to the figure to get the hover functionality, there we also need to add all renderers to the hover by hand if we create line plots later on
still haven't found a way to make the hover tool itself visible when we add it to the toolbar; it does appear when we draw a new line,
if we change edit/del and add lines, (including their renderers, we need to del/add those renderes to the hover tools as well
"""
renderers = []
#check if lines have changed:
if self.hoverTool: ###kna
newLines = set([v for k,v in self.lines.items() if not self.server.is_score_variable(k) ]) # only the non-score lines
newEventLines = set([ v["renderer"] for k,v in self.eventLines.items()])
newLines.update(newEventLines)
newLines.update(set(self.annoHovers))
hoverLines = set(self.hoverTool.renderers)
if newLines != hoverLines or force:
self.logger.debug(f"reset hover tool MUSt UPDATE newLines {newLines}, hoverLines{hoverLines}")
self.hoverTool.renderers = []
store = self.toolBarBox.toolbar.tools
newTools = []
for entry in self.toolBarBox.toolbar.tools:
if type(entry) != HoverTool:
newTools.append(entry)
self.toolBarBox.toolbar.tools = newTools
self.hoverTool = None
if not self.hoverTool or newLines != hoverLines or force:
renderers = []
for k, v in self.eventLines.items():
self.logger.debug(f"add {k} to hover")
renderers.append(v["renderer"])
for k, v in self.lines.items():
if not self.server.is_score_variable(k):
self.logger.debug(f"add line {k} t hover {type(v)} {type(v) is GlyphRenderer}")
if type(v) is GlyphRenderer:
renderers.append(v)
self.logger.info(f"number of new hovers {len(renderers)}")
#for h in self.annoHovers:
# #print(f"add hover {h}")
# renderers.append(h)
for h in self.annoHovers:
renderers.append(h)
#reverse the renderers to give the lines the prio
if renderers:
renderers.reverse()
if not self.hoverTool:
#we do this only once
self.logger.info("MAKE TOOLTIPS"+str(self.hoverCounter))
hover = HoverTool(renderers=renderers) #must apply them here to be able to dynamically change them
#hover.tooltips = [("name","$name"),("time", "@__time{%Y-%m-%d %H:%M:%S.%3N}"),("value","@$name{0.000}")] #show one digit after dot
#hover.tooltips = [("name", "$name"), ("time", "@{__time}{%f}"),
# ("value", "@$name{0.000}")] # show one digit after dot
#hover.tooltips = [("name", "$name"), ("time", "@{x}{%f}"),
# ("value", "@y{0.000}")] # show one digit after dot
hover.tooltips = [("name", "$name"), ("time", "$x{%f}"),
("value", "@y{0.000}")] # show one digi
if 0:
mytooltip = """
< script>
//.bk-tooltip>div:not(:first-child) {display:none;}
console.log("hier hallo");
< /script>
< b>X: < /b> @x < br>
< b>Y: < /b> @y
"""
#hover.tooltips = mytooltip
#hover.formatters={'__time': 'datetime'}
#custom = """var local = moment(value).tz('%s'); return local.format();"""%self.server.get_settings()["timeZone"]
mirror = self.server.get_mirror()
if "xAxisType" in mirror and mirror["xAxisType"][".properties"]["value"]=="number":
if "xAxisUnit" in mirror:
custom = """ return value +" %s";"""%mirror["xAxisUnit"][".properties"]["value"]
else:
custom = """ return value;""" #just return the plain number
else:
custom = """var local = moment(value).tz('%s'); return local.format();"""%self.server.get_settings()["timeZone"]
#custom2 = """var neu;neu = source.data['test'][0]; return String(value);"""
#self.testSource = ColumnDataSource({"test":[67]*1000})
hover.formatters = {'__time': CustomJSHover(code=custom)}
custom3 = """ console.log(cb_data);"""
hover.formatters = {'$x': CustomJSHover(code=custom)}#, 'z':CustomJSHover(args=dict(source=self.testSource),code=custom2)} #xxxkna
#hover.callback=CustomJS(code=custom3)
if self.server.get_settings()["hasHover"] in ['vline','hline','mouse']:
hover.mode = self.server.get_settings()["hasHover"]
hover.mode = "mouse"
hover.line_policy = 'interp'#need this instead of nearest for the event lines: they end in +- infinity, with the "nearest", they would show their tooltip hover at the end of their line, outside the visible area
hover.point_policy = "follow_mouse"
self.plot.add_tools(hover)
self.hoverTool = hover
self.toolBarBox.toolbar.tools.append(hover) # apply he hover tool to the toolbar
# we do this every time
# reapply the renderers to the hover tool
if 0:
renderers = []
self.hoverTool.renderers = []
renderers = []
for k, v in self.lines.items():
if not self.server.is_score_variable(k):
self.logger.debug(f"add line {k} t hover")
renderers.append(v)
self.hoverTool.renderers = renderers
def stream_update_backgrounds(self):
0
View Source File : varstatistics.py
License : MIT License
Project Creator : smartyal
License : MIT License
Project Creator : smartyal
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 and (anno["startTime"] < anno["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