Itay Livni
Itay Livni

Reputation: 2188

Using bokeh: How does one plot variable size nodes, and node colors?

I am trying to display a graph using networkx with bokeh 12.7 that

  1. Has node size based on node degrees
  2. Color based on another node attribute.

Desired Output:

enter image description here

Data Setup

import pandas as pd
import numpy as np
import networkx as nx
import seaborn as sns

from bokeh.io import show, output_notebook  #output_file,
from bokeh.plotting import figure
from bokeh.models.graphs import from_networkx

from bokeh.models import GraphRenderer, StaticLayoutProvider, LinearColorMapper, ColumnDataSource
from bokeh.palettes import Spectral8, Spectral4

G = nx.karate_club_graph()

# Some Random index
node_color = {k:v for k, v  in enumerate(np.random.uniform(low=0, high=21, size=(G.number_of_nodes(),)).round(1))}

# Set node attributes
nx.set_node_attributes(G, 'node_color', node_color)
nx.set_node_attributes(G, 'node_size', G.degree())

Try to graph using bokeh with cubehelix_palette

# Map cubehelix_palette
palette = sns.cubehelix_palette(21)
pal_hex_lst = palette.as_hex()

mapper = LinearColorMapper(palette=pal_hex_lst, low=0, high=21)

# Initiate bokeh plot
plot = figure(title="Resized Node Demo", x_range=(-1.1,1.1), y_range=(-1.1,1.1),
              tools="", toolbar_location=None)

# Graph renderer using nx
graph = from_networkx(G, nx.spring_layout, scale=2, center=(0,0))

# Style node
graph.node_renderer.glyph = Circle(size='node_size', fill_color={'field': 'node_color', 'transform': mapper})


plot.renderers.append(graph)

output_notebook()
#output_file("networkx_graph.html")
show(plot)

Which gives this error: Glyph refers to nonexistent column name

enter image description here

I also tried this:

# 1. Create Plot container
plot = figure(title=endNode, x_range=(-1.1,1.1), y_range=(-1.1,1.1),
              tools="", toolbar_location=None)

# 2. Create graph plot comtainer
graph = GraphRenderer()

node_link_dict = nx.readwrite.json_graph.node_link_data(G)
node_df = pd.DataFrame(node_link_dict['nodes'])

node_cds = ColumnDataSource.from_df(graph_data.node_df)
graph.node_renderer.data_source.data = node_cds


# 3. Set Node Style
graph.node_renderer.glyph = Circle(size='node_size', fill_color='node_color')

Any thoughts?

Upvotes: 4

Views: 4814

Answers (2)

AG_exp
AG_exp

Reputation: 129

Was looking for something similar. Maybe it helps someone else.

You can change it in the same way as changing the node size. So there is no need to use pandas which could also mix up sth. if used in the wrong way.

You can create a list where the colors for each node is stored. Then add the color information:

colors = [...]
graph.node_renderer.data_source.data['colors'] = colors
graph.node_renderer.glyph = Circle(size='degrees', fill_color='colors')

Or if you really want to use node attributes then you can do this using a dict: create a list with all colors first:

colors = [...]
colors = dict(zip(G.nodes, colors))
nx.set_node_attributes(G, {k:v for k,v in colors.items()},'colors' )
graph.node_renderer.glyph = Circle(size='degrees', fill_color='colors')

Upvotes: 1

Aswani kumar Arisetty
Aswani kumar Arisetty

Reputation: 46

I was trying to set the node size based on the degree centrality as well and was able to do so using

graph.node_renderer.data_source = source

I can see the differing sizes and colors (see attached image), although I could not find the reason for the below error yet

E-1010 (CDSVIEW_SOURCE_DOESNT_MATCH): CDSView used by Glyph renderer must have a source that matches the Glyph renderer's data source: GlyphRenderer(id='035dd78a-7bff-40d1-8357-d7193222ca02', ...)

    #just to make the sizes visible
    node_size = {k:5*v for k,v in G.degree().items()} 


### set node attributes
    nx.set_node_attributes(G, 'node_color', node_color)
    nx.set_node_attributes(G, 'node_size', node_size)

    source=ColumnDataSource(pd.DataFrame.from_dict({k:v for k,v in G.nodes(data=True)},orient='index'))
    mapper = LinearColorMapper(palette=pal_hex_lst, low=0, high=21)

### Initiate bokeh plot
    plot = figure(title="Resized Node Demo", x_range=(-1.1,1.1), y_range=(-1.1,1.1),
              tools="", toolbar_location=None)

    # Graph renderer using nx
    graph = from_networkx(G, nx.spring_layout, scale=2, center=(0,0))

    # Style node
    graph.node_renderer.data_source = source
    graph.node_renderer.glyph = Circle(size='node_size', fill_color={'field': 'node_color', 'transform': mapper})


    plot.renderers.append(graph)

The zPGap Image

Upvotes: 3

Related Questions