【问题标题】:Get selected data contained within box select tool in Bokeh获取 Bokeh 中框选择工具中包含的选定数据
【发布时间】:2016-03-13 21:16:42
【问题描述】:

如果我在散景中有散点图并且启用了框选择工具,假设我使用框选择工具选择了几个点。如何访问我选择的点的 (x,y) 位置信息?

%matplotlib inline
import numpy as np
from random import choice
from string import ascii_lowercase

from bokeh.models.tools import *
from bokeh.plotting import *

output_notebook()


TOOLS="pan,wheel_zoom,reset,hover,poly_select,box_select"
p = figure(title = "My chart", tools=TOOLS)
p.xaxis.axis_label = 'X'
p.yaxis.axis_label = 'Y'

source = ColumnDataSource(
    data=dict(
        xvals=list(range(0, 10)),
        yvals=list(np.random.normal(0, 1, 10)),
        letters = [choice(ascii_lowercase) for _ in range(10)]
    )
)
p.scatter("xvals", "yvals",source=source,fill_alpha=0.2, size=5)

select_tool = p.select(dict(type=BoxSelectTool))[0]

show(p)

# How can I know which points are contained in the Box Select Tool?

我不能调用“callback”属性,而“dimensions”属性只返回一个列表[“width”,“height”]。如果我能得到选定框的尺寸和位置,我就可以从那里找出我的数据集中有哪些点。

【问题讨论】:

    标签: python bokeh


    【解决方案1】:

    您可以在 ColumnDataSource 上使用 callback,它使用所选数据的索引更新 Python 变量:

    %matplotlib inline
    import numpy as np
    from random import choice
    from string import ascii_lowercase
    
    from bokeh.models.tools import *
    from bokeh.plotting import *
    from bokeh.models import CustomJS
    
    
    
    output_notebook()
    
    
    TOOLS="pan,wheel_zoom,reset,hover,poly_select,box_select"
    p = figure(title = "My chart", tools=TOOLS)
    p.xaxis.axis_label = 'X'
    p.yaxis.axis_label = 'Y'
    
    source = ColumnDataSource(
        data=dict(
            xvals=list(range(0, 10)),
            yvals=list(np.random.normal(0, 1, 10)),
            letters = [choice(ascii_lowercase) for _ in range(10)]
        )
    )
    p.scatter("xvals", "yvals",source=source,fill_alpha=0.2, size=5)
    
    select_tool = p.select(dict(type=BoxSelectTool))[0]
    
    source.callback = CustomJS(args=dict(p=p), code="""
            var inds = cb_obj.get('selected')['1d'].indices;
            var d1 = cb_obj.get('data');
            console.log(d1)
            var kernel = IPython.notebook.kernel;
            IPython.notebook.kernel.execute("inds = " + inds);
            """
    )
    
    show(p)
    

    然后您可以使用类似的方法访问所需的数据属性

    zip([source.data['xvals'][i] for i in inds],
        [source.data['yvals'][i] for i in inds])
    

    【讨论】:

    • 太棒了!我只需要将 CustomJS 更改为“回调”(仍然是 0.9.0 版)。谢谢你的帮助,真的很有用。
    • @FrankFineis:很高兴它有帮助!随意投票(通过单击三角形)并接受答案(通过单击对勾)以从“未回答”列表中删除问题(并给我一些互联网积分=)
    【解决方案2】:

    这是一个使用 Python 3.7.5 和 Bokeh 1.4.0 的工作示例

    此 jupyter 笔记本的公共 github 链接:
    https://github.com/surfaceowl-ai/python_visualizations/blob/master/notebooks/bokeh_save_linked_plot_data.ipynb

    环境报告:

    虚拟环境 python 版本:Python 3.7.5
    虚拟环境 ipython 版本:7.9.0

    水印包报告:

    散景 1.4.0
    jupyter 1.0.0
    numpy 1.17.4
    熊猫 0.25.3
    崛起 5.6.0
    水印 2.0.2

    # Generate linked plots + TABLE displaying data + save button to export cvs of selected data
    
    from random import random
    
    from bokeh.io import output_notebook  # prevent opening separate tab with graph
    from bokeh.io import show
    
    from bokeh.layouts import row
    from bokeh.layouts import grid
    from bokeh.models import CustomJS, ColumnDataSource
    from bokeh.models import Button  # for saving data
    from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
    from bokeh.models import HoverTool
    from bokeh.plotting import figure
    
    
    # create data
    x = [random() for x in range(500)]
    y = [random() for y in range(500)]
    
    # create first subplot
    plot_width = 400
    plot_height = 400
    
    s1 = ColumnDataSource(data=dict(x=x, y=y))
    fig01 = figure(
        plot_width=plot_width,
        plot_height=plot_height,
        tools=["lasso_select", "reset", "save"],
        title="Select Here",
    )
    fig01.circle("x", "y", source=s1, alpha=0.6)
    
    # create second subplot
    s2 = ColumnDataSource(data=dict(x=[], y=[]))
    
    # demo smart error msg:  `box_zoom`, vs `BoxZoomTool`
    fig02 = figure(
        plot_width=400,
        plot_height=400,
        x_range=(0, 1),
        y_range=(0, 1),
        tools=["box_zoom", "wheel_zoom", "reset", "save"],
        title="Watch Here",
    )
    
    fig02.circle("x", "y", source=s2, alpha=0.6, color="firebrick")
    
    # create dynamic table of selected points
    columns = [
        TableColumn(field="x", title="X axis"),
        TableColumn(field="y", title="Y axis"),
    ]
    
    table = DataTable(
        source=s2,
        columns=columns,
        width=400,
        height=600,
        sortable=True,
        selectable=True,
        editable=True,
    )
    
    # fancy javascript to link subplots
    # js pushes selected points into ColumnDataSource of 2nd plot
    # inspiration for this from a few sources:
    # credit: https://stackoverflow.com/users/1097752/iolsmit via: https://stackoverflow.com/questions/48982260/bokeh-lasso-select-to-table-update
    # credit: https://stackoverflow.com/users/8412027/joris via: https://stackoverflow.com/questions/34164587/get-selected-data-contained-within-box-select-tool-in-bokeh
    
    s1.selected.js_on_change(
        "indices",
        CustomJS(
            args=dict(s1=s1, s2=s2, table=table),
            code="""
            var inds = cb_obj.indices;
            var d1 = s1.data;
            var d2 = s2.data;
            d2['x'] = []
            d2['y'] = []
            for (var i = 0; i < inds.length; i++) {
                d2['x'].push(d1['x'][inds[i]])
                d2['y'].push(d1['y'][inds[i]])
            }
            s2.change.emit();
            table.change.emit();
    
            var inds = source_data.selected.indices;
            var data = source_data.data;
            var out = "x, y\\n";
            for (i = 0; i < inds.length; i++) {
                out += data['x'][inds[i]] + "," + data['y'][inds[i]] + "\\n";
            }
            var file = new Blob([out], {type: 'text/plain'});
    
        """,
        ),
    )
    
    # create save button - saves selected datapoints to text file onbutton
    # inspriation for this code:
    # credit:  https://stackoverflow.com/questions/31824124/is-there-a-way-to-save-bokeh-data-table-content
    # note: savebutton line `var out = "x, y\\n";` defines the header of the exported file, helpful to have a header for downstream processing
    
    savebutton = Button(label="Save", button_type="success")
    savebutton.callback = CustomJS(
        args=dict(source_data=s1),
        code="""
            var inds = source_data.selected.indices;
            var data = source_data.data;
            var out = "x, y\\n";
            for (i = 0; i < inds.length; i++) {
                out += data['x'][inds[i]] + "," + data['y'][inds[i]] + "\\n";
            }
            var file = new Blob([out], {type: 'text/plain'});
            var elem = window.document.createElement('a');
            elem.href = window.URL.createObjectURL(file);
            elem.download = 'selected-data.txt';
            document.body.appendChild(elem);
            elem.click();
            document.body.removeChild(elem);
            """,
    )
    
    # add Hover tool
    # define what is displayed in the tooltip
    tooltips = [
        ("X:", "@x"),
        ("Y:", "@y"),
        ("static text", "static text"),
    ]
    
    fig02.add_tools(HoverTool(tooltips=tooltips))
    
    # display results
    # demo linked plots
    # demo zooms and reset
    # demo hover tool
    # demo table
    # demo save selected results to file
    
    layout = grid([fig01, fig02, table, savebutton], ncols=3)
    
    output_notebook()
    show(layout)
    
    # things to try:
    # select random shape of blue dots with lasso tool in 'Select Here' graph
    # only selected points appear as red dots in 'Watch Here' graph -- try zooming, saving that graph separately
    # selected points also appear in the table, which is sortable
    # click the 'Save' button to export a csv
    
    # TODO:  export from Bokeh to pandas dataframe
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-25
      • 1970-01-01
      • 1970-01-01
      • 2019-03-23
      • 1970-01-01
      • 2013-09-06
      • 2015-01-27
      • 2021-01-24
      相关资源
      最近更新 更多