Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separate node and edge hover tools in Bokeh?

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!

like image 900
ivirshup Avatar asked Nov 07 '22 00:11

ivirshup


1 Answers

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

like image 75
Jmegan042 Avatar answered Nov 15 '22 12:11

Jmegan042