【问题标题】:Bokeh hovertools which run arbitrary python code运行任意 python 代码的 Bokeh hovertools
【发布时间】:2019-03-19 15:04:12
【问题描述】:

我正在使用 Bokeh 尝试创建一个图形,其数据点在用户“悬停”时将在悬停工具中显示另一个图形,显示有关该数据点的其他信息(即,在主图中数据点是设定间隔内时间序列的平均值,我希望悬停工具显示该间隔内的所有数据)。

user guide(完整代码复制在下面)提供了一种解决方案:使用自定义 HTML 工具提示来引用文件中的数字。但是,这需要我创建所有存档的数字(可能多达 10,000 个)以供参考。这是一个太大的时间开销,所以我希望有一个更好的解决方案。 即:悬停工具是否可以动态运行 python 代码,以便它们可以交互地显示数据图?

(示例图片,取自用户指南,以下代码)

以下代码是 2019 年 3 月 19 日从bokeh user guide 复制而来的。

from bokeh.plotting import figure, output_file, show, ColumnDataSource

output_file("toolbar.html")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    desc=['A', 'b', 'C', 'd', 'E'],
imgs=[
    'http://docs.bokeh.org/static/snake.jpg',
    'http://docs.bokeh.org/static/snake2.png',
    'http://docs.bokeh.org/static/snake3D.png',
     'http://docs.bokeh.org/static/snake4_TheRevenge.png',
    'http://docs.bokeh.org/static/snakebite.jpg'
],
fonts=[
    '<i>italics</i>',
    '<pre>pre</pre>',
    '<b>bold</b>',
    '<small>small</small>',
    '<del>del</del>'
]
))

TOOLTIPS = """
<div>
    <div>
        <img
            src="@imgs" height="42" alt="@imgs" width="42"
            style="float: left; margin: 0px 15px 15px 0px;"
            border="2"
        ></img>
    </div>
    <div>
        <span style="font-size: 17px; font-weight: bold;">@desc</span>
        <span style="font-size: 15px; color: #966;">[$index]</span>
    </div>
    <div>
        <span>@fonts{safe}</span>
    </div>
    <div>
        <span style="font-size: 15px;">Location</span>
        <span style="font-size: 10px; color: #696;">($x, $y)</span>
    </div>
</div>
"""

p = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,
       title="Mouse over the dots")

p.circle('x', 'y', size=20, source=source)

show(p)

【问题讨论】:

    标签: python bokeh


    【解决方案1】:

    您只能在 Bokeh 服务器应用程序中使用 Python 回调。似乎不可能对 HoverTool 使用 Python 回调(它必须始终是 JS 回调,否则您会收到此错误:ValueError: expected an instance of type Callback, got &lt;function callback at 0x114fdbb90&gt; of type function)。

    以下解决方案使用 JS 回调,并在将圆圈悬停在主图上时显示一个小的“工具提示图”(适用于 Bokeh v1.0.4,并且仅当 Bokeh 中有 2 个图时文件):

    from bokeh.plotting import figure, show
    from bokeh.layouts import gridplot, Row
    from bokeh.models import ColumnDataSource, CDSView, BooleanFilter, CustomJS, BoxSelectTool, HoverTool
    import pandas as pd
    
    data = {'x': [1, 2, 3],
            'y':[1, 2, 3],
            'xs':[[9, 8, 7], [6, 5, 4], [3, 2, 1]],
            'ys':[[29, 28, 29], [27, 28, 27], [25, 25, 20]]}
    source = ColumnDataSource(data)
    plot = figure(title = 'PLOT IN HOVER TOOLTIP', tools = '')
    circles = plot.circle('x', 'y', size = 20, source = source)
    
    plot_tooltip = figure(name = 'plot_tooltip', plot_width = 200, plot_height = 200, x_axis_location = None, y_axis_location = None, title = None, tools = 'hover', tooltips = [("x", "@x"), ("y", "@y")], toolbar_location = None)
    lines = plot_tooltip.line('x', 'y', source = ColumnDataSource({'x': [], 'y': []}))
    circles2 = plot_tooltip.circle('x', 'y', source = ColumnDataSource({'x': [], 'y': []}))
    
    code = """  
    var indices = cb_data.index['1d'].indices;
    if (indices.length > 0){
        if(plot_tooltip.x_range.bounds == null)
        {
            Bokeh.documents[0].add_root(plot_tooltip)
        }
        const idx = indices[0]
        lines.data_source.data['x'] = source.data['xs'][idx]
        lines.data_source.data['y'] = source.data['ys'][idx]
        lines.data_source.change.emit();
    
        circles.data_source.data['x'] = source.data['xs'][idx]
        circles.data_source.data['y'] = source.data['ys'][idx]
        circles.data_source.change.emit();  
    
        div = document.getElementsByClassName('bk-root')[1];
        div.style = "position:absolute; left:" + cb_data.geometry['sx'] + "px; top:" + cb_data.geometry['sy'] + "px;";              
    } """
    
    callback = CustomJS(args = dict(source = source, lines = lines, circles = circles2, plot_tooltip = plot_tooltip), code = code)
    
    hover = HoverTool()
    hover.callback = callback
    hover.tooltips = None
    hover.renderers = [circles]
    plot.add_tools(hover)
    
    show(plot)
    

    结果:

    【讨论】:

    • 感谢您回答托尼,如果没有其他答案出现,我会在适当的时候接受它。我确实担心这样的实施是必要的。遗憾的是,它不能轻易完成,因为在我的代码中,悬停的数字看起来会更干净。
    • 这很好,谢谢!我将为一些示例数据集实现它,并与您之前的建议进行比较(主图旁边的静态图)。我将在选项卡中工作,此选项卡上只有两个数字,所以我可以让它适用于我的解决方案。
    • 经过反思,虽然这确实具有所需的功能,但它需要使用 JavaScript 回调,如果您可以在回调中使用 Python,那就太好了。我将对回调做更多的研究,因为我从来没有做过...... 编辑: 看起来基本上 JS 必须用于回调,但有一些函数可能有用:@987654322 @
    • from_py_func() 只能包含可以轻松转换为 BokehJS 的简单 JS 代码。它无法满足您的要求。
    猜你喜欢
    • 2015-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-23
    • 2011-01-27
    • 1970-01-01
    • 2012-07-15
    相关资源
    最近更新 更多