Get selected data contained within box select tool in Bokeh

前端 未结 2 847
挽巷
挽巷 2020-12-03 03:48

If I have a scatter plot in bokeh and I\'ve enabled the Box Select Tool, suppose I select a few points with the Box Select Tool. How can I access the (x,y) position location

相关标签:
2条回答
  • 2020-12-03 04:31

    You can use a callback on the ColumnDataSource that updates a Python variable with the indices of the selected data:

    %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)
    

    Then you can access the desired data attributes using something like

    zip([source.data['xvals'][i] for i in inds],
        [source.data['yvals'][i] for i in inds])
    
    0 讨论(0)
  • 2020-12-03 04:33

    Here is a working example with Python 3.7.5 and Bokeh 1.4.0

    public github link to this jupyter notebook:
    https://github.com/surfaceowl-ai/python_visualizations/blob/master/notebooks/bokeh_save_linked_plot_data.ipynb

    environment report:

    virtual env python version: Python 3.7.5
    virtual env ipython version: 7.9.0

    watermark package reports:

    bokeh 1.4.0
    jupyter 1.0.0
    numpy 1.17.4
    pandas 0.25.3
    rise 5.6.0
    watermark 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
    
    0 讨论(0)
提交回复
热议问题