def plot(tables, output_filename):
    '''
    This is the plot function that uses Bokeh functions and widgets to make an interactive hexagon plot.

    This function recieves:
    - tables: dictionary with tables used to create arrays of repeated x, y coordinates (depending on the counts) for the hexagon plot.
    - output_filename: filename of .html output in the plots folder

    The coordinate arrays are used to create a pandas dataframe with Bokeh functions. This dataframe contains the q, r coordinates and counts used to plot the
    hexagons. To this dataframe, extra information is added (e.g. most common chemicals), which is displayed in the hover tooltip.

    Gaussian blur is added to copies of this dataframe and given as input to the Bokeh slider widget.
    Other widgets are added as well, for saturation, normalisation etc. Bokeh allows to customize these widges with javascript code.

    The hexagon plot is saved as a .html file and also shown in the browser.
    '''

    file_name = 'plots/' + str(output_filename) + '.html'
    output_file(file_name)

    # Blur and saturation values
    BLUR_MAX = 3
    BLUR_STEP_SIZE = 1
    SATURATION_MAX = 5
    SATURATION_STEP_SIZE = 0.25

    # First, create array for plot properties ( ratio, size of hexagons etc.)
    default_term = list(tables.keys())[0]
    x, y, ids = create_array(tables[default_term]['table'],
                             normalisation=False)

    # Hexagon plot properties
    length = len(x)
    orientation = 'flattop'
    ratio = ((max(y) - min(y)) / (max(x) - min(x)))
    size = 10 / ratio
    h = sqrt(3) * size
    h = h * ratio
    title = 'Hexbin plot for ' + str(
        length) + ' annotated chemicals with query ' + str(default_term)

    # make figure
    p = figure(title=title,
               x_range=[min(x) - 0.5, max(x) + 0.5],
               y_range=[0 - (h / 2), max(y) + 100],
               tools="wheel_zoom,reset,save",
               background_fill_color='#440154')

    p.grid.visible = False
    p.xaxis.axis_label = "log(P)"
    p.yaxis.axis_label = "mass in Da"
    p.xaxis.axis_label_text_font_style = 'normal'
    p.yaxis.axis_label_text_font_style = 'normal'

    # source for plot
    term_to_source, term_to_metadata, options = make_plot_sources(
        tables, size, ratio, orientation, BLUR_MAX, BLUR_STEP_SIZE)

    # start source for plot, this is the source that is first displayed in the hexagon figure
    x, y, ids = create_array(tables[default_term]['table'],
                             normalisation=False)
    df = hexbin(x, y, ids, size, aspect_scale=ratio, orientation=orientation)
    df = add_counts(df, tables[default_term]['table'])
    source = ColumnDataSource(df)
    metadata = term_to_metadata[default_term]
    metadata = return_html(metadata)

    # color mapper
    mapper = linear_cmap('scaling', 'Viridis256', 0,
                         max(source.data['scaling']))

    # plot
    hex = p.hex_tile(q="q",
                     r="r",
                     size=size,
                     line_color=None,
                     source=source,
                     aspect_scale=ratio,
                     orientation=orientation,
                     fill_color=mapper)

    # HOVER
    TOOLTIPS = return_tooltip()
    code_callback_hover = return_code('hover')
    callback_hover = CustomJS(code=code_callback_hover)
    hover = HoverTool(tooltips=TOOLTIPS,
                      callback=callback_hover,
                      show_arrow=False)
    p.add_tools(hover)

    # WIDGETS
    slider1 = Slider(start=1,
                     end=SATURATION_MAX,
                     value=1,
                     step=SATURATION_STEP_SIZE,
                     title="Saturation",
                     width=100)
    slider2 = Slider(start=0,
                     end=BLUR_MAX,
                     value=0,
                     step=BLUR_STEP_SIZE,
                     title="Blur",
                     width=100)
    checkbox = CheckboxGroup(labels=["TFIDF"], active=[])
    radio_button_group = RadioGroup(labels=["Viridis256", "Greys256"],
                                    active=0)
    button = Button(label="Metadata", button_type="default", width=100)
    multi_select = MultiSelect(title=output_filename,
                               value=[default_term],
                               options=options,
                               width=100,
                               height=300)

    # WIDGETS CODE FOR CALLBACK
    code_callback_slider1 = return_code('slider1')
    code_callback_slider2 = return_code('slider2')
    code_callback_checkbox = return_code('checkbox')
    code_callback_rbg = return_code('rbg')
    code_callback_button = return_code('button')
    code_callback_ms = return_code('multi_select')

    # WIDGETS CALLBACK
    callback_slider1 = CustomJS(args={
        'source': source,
        'mapper': mapper
    },
                                code=code_callback_slider1)
    callback_slider2 = CustomJS(args={
        'source': source,
        'mapper': mapper,
        'slider1': slider1,
        'multi_select': multi_select,
        'checkbox': checkbox,
        'term_to_source': term_to_source,
        'step_size': BLUR_STEP_SIZE
    },
                                code=code_callback_slider2)
    callback_checkbox = CustomJS(args={
        'source': source,
        'term_to_source': term_to_source,
        'multi_select': multi_select,
        'step_size': BLUR_STEP_SIZE,
        'slider1': slider1,
        'slider2': slider2,
        'mapper': mapper
    },
                                 code=code_callback_checkbox)
    callback_radio_button_group = CustomJS(args={
        'p': p,
        'mapper': mapper,
        'Viridis256': Viridis256,
        'Greys256': Greys256
    },
                                           code=code_callback_rbg)
    callback_button = CustomJS(args={
        'term_to_metadata': term_to_metadata,
        'multi_select': multi_select
    },
                               code=code_callback_button)
    callback_ms = CustomJS(args={
        'source': source,
        'term_to_source': term_to_source,
        'checkbox': checkbox,
        'metadata': metadata,
        'step_size': BLUR_STEP_SIZE,
        'slider2': slider2,
        'slider1': slider1,
        'p': p,
        'mapper': mapper
    },
                           code=code_callback_ms)

    # # WIDGETS INTERACTION
    slider1.js_on_change('value', callback_slider1)
    slider2.js_on_change('value', callback_slider2)
    checkbox.js_on_change('active', callback_checkbox)
    radio_button_group.js_on_change('active', callback_radio_button_group)
    button.js_on_event(events.ButtonClick, callback_button)
    multi_select.js_on_change("value", callback_ms)

    # LAYOUT
    layout = row(
        multi_select, p,
        column(slider1, slider2, checkbox, radio_button_group, button))

    show(layout)
Ejemplo n.º 2
0
def build_graph_plot(G, title=""):
    """ Return a Bokeh plot of the given networkx graph

    Parameters
    ----------
    G: :obj:`networkx.Graph`
        Networkx graph instance to be plotted.
    title: str
        Title of the final plot

    Returns
    -------
    :obj:`bokeh.models.plot`
        Bokeh plot of the graph.
    """

    plot = Plot(plot_width=600,
                plot_height=450,
                x_range=Range1d(-1.1, 1.1),
                y_range=Range1d(-1.1, 1.1))
    plot.title.text = title

    node_attrs = {}
    for node in G.nodes(data=True):
        node_color = Spectral4[node[1]['n']]
        node_attrs[node[0]] = node_color
    nx.set_node_attributes(G, node_attrs, "node_color")

    node_hover_tool = HoverTool(tooltips=[("Label", "@label"), ("n", "@n")])
    wheelZoom = WheelZoomTool()
    plot.add_tools(node_hover_tool, PanTool(), wheelZoom, ResetTool())
    plot.toolbar.active_scroll = wheelZoom

    graph_renderer = from_networkx(G,
                                   nx.spring_layout,
                                   k=0.3,
                                   iterations=200,
                                   scale=1,
                                   center=(0, 0))
    graph_renderer.node_renderer.glyph = Circle(size=15,
                                                fill_color="node_color")
    graph_renderer.edge_renderer.glyph = MultiLine(line_alpha=0.8,
                                                   line_width=1)

    plot.renderers.append(graph_renderer)

    selectCallback = CustomJS(args=dict(graph_renderer=graph_renderer),
                              code="""
            let new_data_nodes = Object.assign({},graph_renderer.node_renderer.data_source.data);
            new_data_nodes['node_color'] = {};
            let colors = ['#2b83ba','#ABDDA4','#fdae61'];
            let ns = cb_obj.value.reduce((acc,v)=>{
                if(v=='fullGraph'){
                   acc.push(0);
                   acc.push(1);
                   acc.push(2);
                }
               if(v=='n0')acc.push(0);
               if(v=='n1')acc.push(1);
               if(v=='n2')acc.push(2);
               return acc;
            },[])


            Object.keys(graph_renderer.node_renderer.data_source.data['node_color']).map((n,i)=>{
                new_data_nodes['node_color'][i]='transparent';
            })


             ns.map(n=>{
                Object.keys(graph_renderer.node_renderer.data_source.data['node_color']).map((g,i)=>{
                    if(graph_renderer.node_renderer.data_source.data['n'][i]==n){
                        new_data_nodes['node_color'][i]=colors[n];
                    }
                })
            })

            graph_renderer.node_renderer.data_source.data = new_data_nodes

            """)

    multi_select = MultiSelect(title="Option:",
                               options=[("fullGraph", "Full Graph"),
                                        ("n0", "Seed Nodes"), ("n1", "N1"),
                                        ("n2", "N2")])
    multi_select.js_on_change('value', selectCallback)

    return column(plot, multi_select)
Ejemplo n.º 3
0
import panel as pn
from bokeh.models import MultiSelect, CustomJS
from pcp.models.multiselect import PCPMultiSelect
from bokeh.sampledata.airports import data

options = [(str(i), str(n)) for i, n in zip(data["name"].index, data["name"])]

m1 = PCPMultiSelect(options=options, height=80, width=400)
m2 = MultiSelect(options=options)
t = pn.widgets.Toggle(name="search")
t.jslink(m1, value="searchbox")

# m1.on_change("value", lambda attr, old, new: setattr(m2,"value", new))
cb1 = CustomJS(args=dict(m2=m2), code="m2.value = cb_obj.value")
m1.js_on_change("value", cb1)
m1.on_change("value", lambda attr, old, new: print("m1", old, new))
cb2 = CustomJS(args=dict(m1=m1), code="m1.value = cb_obj.value")
# m2.on_change("value", lambda attr, old, new: setattr(m1,"value", new))
m2.js_on_change("value", cb2)
m2.on_change("value", lambda attr, old, new: print("m2", old, new))
pn.Row(m1, m2, t).show()
Ejemplo n.º 4
0
def plot(tables, output_filename, xmin, xmax, ymin, ymax, superterm):
    '''
    This is the plot function that uses Bokeh functions and widgets to make an interactive hexagon plot.

    This function recieves:
    - tables: dictionary with tables used to create arrays of repeated x, y coordinates (depending on the counts) for the hexagon plot.
    - output_filename: filename of .html output in the plots folder

    The coordinate arrays are used to create a pandas dataframe with Bokeh functions. This dataframe contains the q, r coordinates and counts used to plot the
    hexagons. To this dataframe, extra information is added (e.g. most common chemicals), which is displayed in the hover tooltip.

    Gaussian blur is added to copies of this dataframe and given as input to the Bokeh slider widget.
    Other widgets are added as well, for saturation, normalization etc. Bokeh allows to customize these widges with javascript code.

    The hexagon plot is saved as a .html file and also shown in the browser.
    '''

    file_name = 'plots/' + str(output_filename) + '.html'
    output_file(file_name)

    # Blur and saturation values
    BLUR_MAX = 4
    BLUR_STEP_SIZE = 0.25
    SATURATION_MAX = 5
    SATURATION_STEP_SIZE = 0.25

    # Hexagon plot properties
    SIZE_HEXAGONS = 10
    orientation = 'flattop'  #bokeh alows 2 different hexagon orientations which also influences hexagon size calculations, but we currently have only calculated blur distances for this orientation
    ratio = ((ymax - ymin) / (xmax - xmin))
    size = SIZE_HEXAGONS / ratio
    hexagon_height = sqrt(3) * size
    hexagon_height = hexagon_height * ratio

    # make figure
    p = figure(x_range=[xmin, xmax],
               y_range=[ymin - (hexagon_height / 2), ymax],
               tools="wheel_zoom,reset,save",
               background_fill_color='#440154')

    p.grid.visible = False
    p.xaxis.axis_label = "log(P)"
    p.yaxis.axis_label = "mass in Da"
    p.xaxis.axis_label_text_font_style = 'normal'
    p.yaxis.axis_label_text_font_style = 'normal'

    # term_to_source, term_to_metadata, options = make_plot_sources(tables, size, ratio, orientation, BLUR_MAX, BLUR_STEP_SIZE)

    # source for widgets
    term_to_source = dict()
    term_to_class = dict()
    term_to_metadata = dict()
    options = []

    for term in tables.keys():
        options.append((term, term))
        table = tables[term]['table']
        if superterm:
            source = create_class_source(table, term, size, ratio, orientation,
                                         superterm)
            term_to_class[term] = {}
            term_to_class[term]['show_class'] = True
            term_to_class[term]['source'] = source
        else:
            term_to_class[term] = {'show_class': False}
        source, title = create_data_source(table, term, size, ratio,
                                           orientation, BLUR_MAX,
                                           BLUR_STEP_SIZE)
        metadata = return_html(tables[term]['metadata'])
        term_to_source[term] = {'source': source, 'title': title}
        term_to_metadata[term] = metadata

    # hex = p.hex_tile(q='q', r="r", size=size, line_color=None, source=source, aspect_scale=ratio,orientation=orientation,
    # fill_color='pink' )

    # show(p)

    # make default souce for plot, this is the first source shown in the plot, and also works like a container. Old data is thrown out and new data is thrown in.
    default_term = list(tables.keys())[0]  # pick the first one
    metadata = tables[default_term]['metadata']
    metadata = return_html(metadata)
    table = tables[default_term]['table']
    source, title = create_data_source(table, default_term, size, ratio,
                                       orientation, BLUR_MAX, BLUR_STEP_SIZE)
    p.title.text = title

    # color mapper
    mapper = linear_cmap('scaling', 'Viridis256', 0,
                         max(source.data['scaling']))

    # plot
    hex = p.hex_tile(q="q",
                     r="r",
                     size=size,
                     line_color=None,
                     source=source,
                     aspect_scale=ratio,
                     orientation=orientation,
                     fill_color=mapper)
    if superterm:
        source_class = term_to_class[default_term]['source']
        class_hex = p.hex_tile(q='q',
                               r="r",
                               size=size,
                               line_color=None,
                               source=source_class,
                               aspect_scale=ratio,
                               orientation=orientation,
                               fill_color='pink',
                               fill_alpha=0.7)
        class_hex.visible = False
    # HOVER
    TOOLTIPS = return_JS_code('tooltips')
    TOOLTIPS_tfidf = return_JS_code('tooltips_tfidf')
    code_callback_hover = return_JS_code('hover')
    callback_hover = CustomJS(code=code_callback_hover)
    hover = HoverTool(tooltips=TOOLTIPS,
                      callback=callback_hover,
                      show_arrow=False)
    p.add_tools(hover)

    # WIDGETS
    slider1 = Slider(start=1,
                     end=SATURATION_MAX,
                     value=1,
                     step=SATURATION_STEP_SIZE,
                     title="Saturation",
                     width=100)
    slider2 = Slider(start=0,
                     end=BLUR_MAX,
                     value=0,
                     step=BLUR_STEP_SIZE,
                     title="Blur",
                     width=100)
    checkbox = CheckboxGroup(labels=["TFIDF"], active=[])
    radio_button_group = RadioGroup(labels=["Viridis256", "Greys256"],
                                    active=0)
    button = Button(label="Metadata", button_type="default", width=100)
    multi_select = MultiSelect(title=output_filename,
                               value=[default_term],
                               options=options,
                               width=100,
                               height=300)
    if superterm:
        label = "Show " + str(superterm)
        checkbox_class = CheckboxGroup(labels=[label], active=[])

    # WIDGETS CODE FOR CALLBACK
    code_callback_slider1 = return_JS_code('slider1')
    code_callback_slider2 = return_JS_code('slider2')
    code_callback_checkbox = return_JS_code('checkbox')
    code_callback_rbg = return_JS_code('rbg')
    code_callback_button = return_JS_code('button')
    code_callback_ms = return_JS_code('multi_select')
    if superterm:
        code_callback_class = return_JS_code('class')

    # WIDGETS CALLBACK
    callback_slider1 = CustomJS(args={
        'source': source,
        'mapper': mapper,
        'slider2': slider2,
        'checkbox': checkbox
    },
                                code=code_callback_slider1)
    callback_slider2 = CustomJS(args={
        'source': source,
        'mapper': mapper,
        'slider1': slider1,
        'checkbox': checkbox
    },
                                code=code_callback_slider2)
    callback_checkbox = CustomJS(args={
        'source': source,
        'slider1': slider1,
        'slider2': slider2,
        'mapper': mapper,
        'hover': hover,
        'tooltips': TOOLTIPS,
        'tooltips_tfidf': TOOLTIPS_tfidf
    },
                                 code=code_callback_checkbox)
    callback_radio_button_group = CustomJS(args={
        'p': p,
        'multi_select': multi_select,
        'mapper': mapper,
        'term_to_class': term_to_class,
        'Viridis256': Viridis256,
        'Greys256': Greys256
    },
                                           code=code_callback_rbg)
    callback_button = CustomJS(args={
        'term_to_metadata': term_to_metadata,
        'multi_select': multi_select
    },
                               code=code_callback_button)
    callback_ms = CustomJS(args={
        'source': source,
        'term_to_source': term_to_source,
        'term_to_class': term_to_class,
        'checkbox': checkbox,
        'slider2': slider2,
        'slider1': slider1,
        'p': p,
        'mapper': mapper
    },
                           code=code_callback_ms)
    if superterm:
        callback_radio_button_group = CustomJS(args={
            'p': p,
            'multi_select': multi_select,
            'class_hex': class_hex,
            'term_to_class': term_to_class,
            'mapper': mapper,
            'Viridis256': Viridis256,
            'Greys256': Greys256
        },
                                               code=code_callback_rbg)
        callback_class = CustomJS(args={
            'multi_select': multi_select,
            'term_to_class': term_to_class,
            'class_hex': class_hex
        },
                                  code=code_callback_class)
        callback_ms = CustomJS(args={
            'source': source,
            'term_to_source': term_to_source,
            'checkbox': checkbox,
            'slider2': slider2,
            'slider1': slider1,
            'p': p,
            'mapper': mapper,
            'checkbox_class': checkbox_class,
            'class_hex': class_hex,
            'term_to_class': term_to_class
        },
                               code=code_callback_ms)

    # WIDGETS INTERACTION
    slider1.js_on_change('value', callback_slider1)
    slider2.js_on_change('value', callback_slider2)
    checkbox.js_on_change('active', callback_checkbox)
    radio_button_group.js_on_change('active', callback_radio_button_group)
    button.js_on_event(events.ButtonClick, callback_button)
    multi_select.js_on_change("value", callback_ms)
    if superterm:
        checkbox_class.js_on_change('active', callback_class)

    # LAYOUT
    if superterm:
        layout = row(
            multi_select, p,
            column(slider1, slider2, checkbox, checkbox_class,
                   radio_button_group, button))
    else:
        layout = row(
            multi_select, p,
            column(slider1, slider2, checkbox, radio_button_group, button))

    show(layout)
from bokeh.io import show
from bokeh.models import CustomJS, MultiSelect

OPTIONS = [("1", "foo"), ("2", "bar"), ("3", "baz"), ("4", "quux")]

multi_select = MultiSelect(value=["1", "2"], options=OPTIONS)
multi_select.js_on_change("value", CustomJS(code="""
    console.log('multi_select: value=' + this.value, this.toString())
"""))

show(multi_select)