bokeh.models.CustomJSHover

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 7

0 View Source File : bokeh.py
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

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

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

    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

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