简体   繁体   中英

Networkx plotting in bokeh: how to set edge width based on graph edge weight

For a given networkx graph G, I would like to adjust the edge line_width as a function of graph weights in Bokeh. Simplified example:

import networkx as nx
import pandas as pd
from bokeh.models import Plot, ColumnDataSource, Range1d, from_networkx, Circle,MultiLine
from bokeh.io import show, output_file
from bokeh.palettes import Viridis

#define graph
source = ['A', 'A', 'A','a','B','B','B','b']
target = ['a', 'B','b','b','a','b','A','a']
weight = [.1,.2,.3,.4,.4,.3, .2, .1]
df = pd.DataFrame([source,target,weight])
df = df.transpose()
df.columns = ['source','target','weight']
G=nx.from_pandas_dataframe(df, 'source', 'target', ['weight'])

#set node attributes
node_color = {'A':Viridis[10][0], 'B':Viridis[10][9],'a':Viridis[10][4],'b':Viridis[10][4]}
node_size = {'A':50, 'B':40,'a':10,'b':10}
node_initial_pos = {'A':(-0.5,0), 'B':(0.5,0),'a':(0,0.25),'b':(0,-0.25)}
nx.set_node_attributes(G, 'node_color', node_color)
nx.set_node_attributes(G, 'node_size', node_size)
nx.set_node_attributes(G, 'node_initial_pos', node_initial_pos)

#source with node color, size and initial pos (perhaps )
node_source = ColumnDataSource(pd.DataFrame.from_dict({k:v for k,v in G.nodes(data=True)}, orient='index'))

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=0.5, center=(0,0))

#style
graph_renderer.node_renderer.data_source = node_source
graph_renderer.node_renderer.glyph = Circle(fill_color = 'node_color',size = 'node_size', line_color = None)

graph_renderer.edge_renderer.glyph = MultiLine(line_color="#CCCCCC", line_alpha=0.8, line_width=2)


plot.renderers.append(graph_renderer)
output_file('test.html')

show(plot)

This provides a plot, but I'm unable to link G's weights to the line_width property of the edge_renderer. Rather than line_width=2, I would like the line_width to be the graph weights. I have tried creating a new ColumnDataSource based off of the df that was passed into G, but no luck. For example, the following gives "Bokeh Error Cannot read property 'length' of undefined":

nodal_relation_df['weight'] = nodal_relation_df['weight'].apply(float)
graph_source = ColumnDataSource(nodal_relation_df)
graph_renderer.edge_renderer.data_source = graph_source

Any other approaches to pairing the graph_renderer.edge_renderer.glyph's line_width to the graph weights?

Bokeh 0.12.14, Networkx 2.1, Python 3.5.4, Jupyter 5.0.0

add following lines before output:

weight_map = dict(zip(zip(source, target), weight))
weight_map.update(zip(zip(target, source), weight))
data = graph_renderer.edge_renderer.data_source.data
data["line_width"] = [weight_map[edge] * 10 for edge in zip(data["start"], data["end"])]
graph_renderer.edge_renderer.glyph.line_width = {'field': 'line_width'}

Given a networkX graph with weighted edges named G and a bokeh plot object named plot , I propose a more compact form:

graph_renderer.edge_renderer.data_source.data["line_width"] = [G.get_edge_data(a,b)['weight'] for a, b in G.edges()]
graph_renderer.edge_renderer.glyph.line_width = {'field': 'line_width'}
plot.renderers.append(graph_renderer)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM