I am looking for a way to pass a color, as assinged in NetworkX's node construction, to a Bokeh plot.
There are some great ways to implement color in the Bokeh plot after it is generated, such as this, but this solution requires that I apply the transformation to the entire data set based on an attribute.
I wanted to do something even simpler and assign a color and size based on what I assign those to be in NetworkX. I normally plot node set 1 as red, then node set 2 as blue in NetworkX, then connect them through their mutual edges. While the colors and size of the nodes are not passed to matplotlib, it IS passed to gephi when I save the file as graphml, so these data are somewhere..
import networkx as nx
from bokeh.io import show, output_file
from bokeh.plotting import figure,show
from bokeh.models.graphs import from_networkx #I haven't been able to use this!
from bokeh.io import output_notebook
from bokeh.models import HoverTool, ColumnDataSource
from bokeh.resources import CDN
from bokeh.embed import file_html
Dataset1 = ['A','B','C','D','E','F']
Dataset2 = ['ONE','TWO','THREE','FOUR','FIVE','TWENTY_EIGHT']
Edgelist = [('A','ONE'),('B','ONE'),('E','ONE'),('A','TWO'),('A','THREE'),('A','FOUR'),('C','THREE'),('D','FIVE'),('D',"TWENTY_EIGHT")]
G = nx.Graph()
G.add_nodes_from(Dataset1,color= 'green')
G.add_nodes_from(Dataset2,color='blue')
G.add_edges_from(Edgelist,weight=0.8)
layout = nx.draw_spring(G, with_labels=True)
nx.write_graphml(G,"TEST.graphML")
network = nx.read_graphml("TEST.graphML")
#start Bokeh code
layout = nx.spring_layout(network,k=1.1/sqrt(network.number_of_nodes()),iterations=100) #pass the NX file to a spring layout
nodes, nodes_coordinates = zip(*sorted(layout.items()))
nodes_xs, nodes_ys = list(zip(*nodes_coordinates))
nodes_source = ColumnDataSource(dict(x=nodes_xs, y=nodes_ys,name=nodes)) #Can this pass the color?
hover = HoverTool(tooltips=[('name', '@name')]) #would like to know how to add in more values here manually
plot = figure(plot_width=800, plot_height=400,tools=['tap', hover, 'box_zoom', 'reset'])
r_circles = plot.circle('x', 'y', source=nodes_source, size=10, color='orange', level = 'overlay')#this function sets the color of the nodes, but how to set based on the name of the node?
def get_edges_specs(_network, _layout):
d = dict(xs=[], ys=[], alphas=[])
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)
# example: { ..., ('user47', 'da_bjoerni', {'weight': 3}), ... }
for u, v, data in _network.edges(data=True):
d['xs'].append([_layout[u][0], _layout[v][0]])
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
show(plot)
When opened in gephi, color is retained:
I can't quite understand how to fetch these values using bokeh's from_networkx function. It seems that this doesn't pass the attributes over as expected. What is actually being passed and how would I pass color?
Is there a better way to just assign more attributes through the ColumnDataSource that is constructed? I'm thinking something akin to passing it to a dataframe, adding a color column, then re-generating the ColumnDataSource, so I can retrieve the colors with '@node_color' for each node value.
I have lists of each of these datasets, so would it be possible to filter somehow such as:
if node_id in list1: node_color = "red" node_size = 10 if node_id in list2: node_color = "blue" node_size = 20
I'm very new to bokeh, and although it seems like these should be easy tasks, I'm completely lost in the documentation. Is it perhaps better to just generate a network using purely bokeh?