Network Graphics in Bokeh: Mapping node connections when you hover over a node

I use Bokeh to create an interactive visualization of NetworkX graphics. I want to show all the faces associated with a particular node when I hover over that node with the mouse.

There is an example in the Bokeh User Guide that does more or less what I want, but I am not satisfied with this solution for two reasons:

  • Line segments are drawn again on each hover event, which means that they are displayed above the node circles, which looks ugly. (This is not noticeable in the example, since the lines and nodes are the same color).
  • My graph is weighted with the width of the rib depending on its weight. There is no way (as far as I know) to get the selected edges to the correct width.

So, I tried a different approach: draw all the edges from the beginning, but set their visible attributes to False . Then create a dictionary with node indices as keys and a list of edges associated with these node as values. When the node is obscured, the edges of the node should get their visible attribute, changed to True . It looks something like this:

 global glob_edges_by_node_index edges_by_node = {} for edge in G.edges(data=True): # create the segments (G is a preexisting NetworkX graph) u,v,d = edge x_0 = pos[u][0] # pos is a preexisting dictionary of x and y values for each node y_0 = pos[u][1] x_1 = pos[v][0] y_1 = pos[v][1] width = 0.03*d['weight'] highlit_edge = p.segment(x0=[x_0],x1=[x_1],y0=[y_0],y1=[y_1],color='#379bdd',line_width=width) # p is a preexisting Bokeh figure highlit_edge.visible = False # all segments are invisible at the start edges_by_node[u].append(highlit_edge) # put the segment into both nodes' dictionary entries edges_by_node[v].append(highlit_edge) id_to_index = {} edges_by_node_index = {} i = 0 for node_id in G.nodes(): # convert the dict keys from their original IDs to the indices seen by Bokeh id_to_index[node_id] = i edges_by_node_index[i] = edges_by_node[node_id] i = i + 1 global glob_edges_by_node_index = edges_by_node_index nodesource = ColumnDataSource(data=dict(x=x,y=y,sizes=nodesizes,colours=nodecolours)) # refers to preexisting data about the nodes cr = p.circle('x','y',color='colours',alpha=0.7,hover_color='colours',hover_alpha=1.0,size='sizes',line_width=1,line_color='#000000',hover_line_color='#000000',source=nodesource) # draw the nodes themselves p.add_tools(HoverTool(tooltips=None,callback=CustomJS.from_py_func(on_hover),renderers=[cr])) def on_hover(window=None): indices = cb_data['index'] for ind in indices: highlit_edges = glob_edges_by_node_index[ind] for highlit_edge in highlit_edges: highlit_edge.visible = True # set edges to visible if they're in the dict entry of the hovered-over node 

This does not work, and I am a little fixated on how to fix it. Especially the use of cb_data is a mystery to me - despite the fact that many of Google could not find clear and comprehensive information about what information cb_data contains, in what format and how to access it. Any help would be greatly appreciated!

+6
source share

All Articles