Separate node and edge hover tools in Bokeh?
Asked Answered
V

1

6

I'm trying to get separate hover tooltips for nodes and edges in Bokeh, but haven't been able to get it to work. Could someone point out what I'm doing wrong? I believe the code should look something like this:

from bokeh.io import show, output_notebook
from bokeh.models import Plot, Range1d, MultiLine, Circle, HoverTool
from bokeh.models.graphs import from_networkx, NodesAndLinkedEdges, EdgesAndLinkedNodes
import networkx as nx
output_notebook()

# Generate data
G = nx.karate_club_graph()
nx.set_edge_attributes(G, nx.edge_betweenness_centrality(G), "betweenness_centrality")

# Setup plot
plot = Plot(plot_width=400, plot_height=400,
            x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1))

graph_renderer = from_networkx(G, nx.spring_layout, scale=1, center=(0, 0))
graph_renderer.node_renderer.glyph = Circle(size=15)
graph_renderer.edge_renderer.glyph = MultiLine(line_alpha=0.8, line_width=1)

plot.renderers.append(graph_renderer)

# Add hover
node_hover_tool = HoverTool(renderers=[graph_renderer.node_renderer], 
                            tooltips=[("index", "@index"), ("club", "@club")])
edge_hover_tool = HoverTool(renderers=[graph_renderer.edge_renderer], 
                            tooltips=[("betweenness_centrality", "@betweenness_centrality")],
                            line_policy="interp")

plot.add_tools(node_hover_tool, edge_hover_tool)

# Show
show(plot)

But I don't see any hover over with this. I've tried a few things to work around this:

  • If I remove the renderers argument, I can get some hover over, but not specific to the glyphs I want.
  • If I remove the renderers argument from both HoverTools, I'm able to get correct tooltips on the nodes along with a betweenness_centrality: ??
  • If I remove the renderers argument from both HoverTools and add graph_renderer.inspection_policy = NodesAndLinkedEdges(), I get correct tooltips on the nodes
  • If I remove the renderers argument from both HoverTools and add graph_renderer.inspection_policy = EdgesAndLinkedNodes(), I get correct tooltips on the edges

I believe this question was asked before on the google group here, but didn't get an answer.

Thanks for any help!

Veronicaveronika answered 16/11, 2018 at 5:23 Comment(0)
A
1

So, we construct our networks differently, but I just solved this problem with one of my Bokeh rendered networks from networkx.

The way that I did it was by generating dataframes with my desired networkx data by using the lines_source approach outlined on another question here, which gives you:

....
plot = figure(
    plot_width=1100, plot_height=700,
    tools=['tap','box_zoom', 'reset']
)  # This is the size of the widget designed.

# This function sets the color of the nodes, but how to set based on the
# name of the node? 
r_circles = plot.circle(
    'x', 'y', source=nodes_source, name= "Node_list",
    size="_size_", fill_color="_color_", level = 'overlay', 
) 

hover = HoverTool(
    tooltips=[('Name', '@name'),('Members','@Members')],
    renderers=[r_circles]
)  # Works to render only the nodes tooltips

def get_edges_specs(_network, _layout): 
    d = dict(xs=[], ys=[], alphas=[],from_node=[],to_node=[])
    weights = [d['weight'] for u, v, d in _network.edges(data=True)]
    max_weight = max(weights)
    calc_alpha = lambda h: 0.1 + 0.6 * (h / max_weight)
    for u, v, data in _network.edges(data=True):
        d['xs'].append([_layout[u][0], _layout[v][0]])
        d['from_node'].append(u)
        d['to_node'].append(v)
        d['ys'].append([_layout[u][1], _layout[v][1]])
        d['alphas'].append(calc_alpha(data['weight']))
    return d

lines_source = ColumnDataSource(get_edges_specs(network, layout))

r_lines = plot.multi_line(
    'xs', 'ys',
    line_width=1.5, alpha='alphas', color='navy',
    source=lines_source
)  # This function sets the color of the edges

Then I generated a hover tool to display the edge information I wanted, so in my case I wanted to know the 'from node' attribute. I also wanted to give it a lofty name, so the tooltip will render "Whered_ya_come_from"

hover2 = HoverTool(
    tooltips=[('Whered_ya_come_from','@from_node')],
    renderers=[r_lines]
)

And then the only difference between how we implement it is that you try to do it as a single addition to the plot, whereas I plot them one after the other.

plot.tools.append(hover1)
# done to append the tool at the end because it has a problem getting
# rendered, as it depended on the nodes being rendered first. 
plot.tools.append(hover2)

From there, you can export it or render it into an HTML file (my preferred method).

Acclimate answered 22/10, 2019 at 23:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.