2

I am attempting to build a network graph using NetworkX and Bokeh. I am using the NetworkX from_pandas_edgelist function to add data for the graph. I would like to color the node of the graph based on the column in the initial data input.

The relation DataFrame is as follows:

company   client

Google    AT&T
Google    Cisco       
Amazon    Facebook
Amazon    Snap
Amazon    Microsoft
Apple     Intel
Apple     IBM
Apple     Visa

The above snippet is only a portion of the DataFrame.

I would like all of the nodes from company to return in a different color to client.

The code below produces a network graph where all nodes are the same color.

G=nx.from_pandas_edgelist(relation, 'company', 'client')

# Show with Bokeh
plot = Plot(plot_width=1000, plot_height=800,
            x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1))
plot.title.text = "Company - Client Network"

node_hover_tool = HoverTool(tooltips=[("Company Name", "@index")])
plot.add_tools(node_hover_tool, BoxZoomTool(), ResetTool())

graph_renderer = from_networkx(G, nx.spring_layout, scale=1, center=(0, 0))

graph_renderer.node_renderer.glyph = Circle(size=20)

graph_renderer.edge_renderer.glyph = MultiLine(line_color="red", line_alpha=0.8, line_width=1)
plot.renderers.append(graph_renderer)

output_file("interactive_graphs.html")
show(plot)

Any assistance anyone could provide would be greatly appreciated.

moe_95
  • 397
  • 2
  • 17
  • [here](https://stackoverflow.com/questions/27030473/how-to-set-colors-for-nodes-in-networkx-python) similar to this guys question, or at least the answer he got seems like it would work for you. – ilamaaa Mar 25 '19 at 16:48
  • Similar yes, but not exactly what I'm looking for. In the given example the first ten nodes are colored green, with the rest blue. Would you know how to color one entire column a specific color, with the other column colored differently? – moe_95 Mar 25 '19 at 16:54
  • 1
    just loop through the nodes and color them based on your criteria in an if statement, in your case i guess it would be an if in column. Might need to turn the column into a series or something to be able to use `in`. – ilamaaa Mar 25 '19 at 17:39

2 Answers2

5

Good question, and accepted answer (from which I was able to extend my code to give colored nodes based on the Pandas dataframe column).

import warnings
warnings.filterwarnings("ignore", category=UserWarning)

import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd

df = pd.read_csv('pers_org.tsv', sep='\t')
# (TSV copied from a PostgreSQL database, hence the "id" column.)
df.sort_values(by=['id'])
'''
      id          person      organization
  0    1  Robert_Bigelow             BAASS
  1    2  Robert_Bigelow             AATIP
  2    3  Robert_Bigelow              NIDS
  3    4  Robert_Bigelow  Skinwalker_Ranch
  14   5   Luis_Elizondo             AATIP
  4    6   Colm_Kelleher             AATIP
  5    7   Colm_Kelleher              NIDS
  6    8   Colm_Kelleher  Skinwalker_Ranch
  7    9     Tom_DeLonge              TTSA
  8   10   Luis_Elizondo              TTSA
  9   11     Hal_Puthoff              TTSA
  10  12    Chris_Mellon              TTSA
  11  13   Douglas_Kurth           US_Navy
  12  14   Douglas_Kurth          Lockheed
  13  15   Douglas_Kurth             BAASS
'''

G = nx.from_pandas_edgelist(df, source='person', target='organization', \
    create_using=nx.DiGraph)
colors = []
for node in G:
    if node in df["person"].values:
        colors.append("lightblue")
    else: colors.append("lightgreen")

print(colors)
# ['lightblue', 'lightgreen', 'lightgreen', 'lightgreen', 'lightgreen',
#  'lightblue', 'lightblue', 'lightgreen', 'lightblue', 'lightblue',
#  'lightblue', 'lightblue', 'lightgreen', 'lightgreen']

plt.figure(figsize=(15,10))
# <Figure size 1500x1000 with 0 Axes>

nx.draw(G, pos = nx.nx_pydot.graphviz_layout(G), \
    node_size=1200, node_color=colors, linewidths=0.25, \
    font_size=10, font_weight='bold', with_labels=True)
plt.show()

See also How to set colors for nodes in networkx python?

pers_org.tsv

id  person  organization
1   Robert_Bigelow  BAASS
2   Robert_Bigelow  AATIP
3   Robert_Bigelow  NIDS
4   Robert_Bigelow  Skinwalker_Ranch
5   Luis_Elizondo   AATIP
6   Colm_Kelleher   AATIP
7   Colm_Kelleher   NIDS
8   Colm_Kelleher   Skinwalker_Ranch
9   Tom_DeLonge TTSA
10  Luis_Elizondo   TTSA
11  Hal_Puthoff TTSA
12  Chris_Mellon    TTSA
13  Douglas_Kurth   US_Navy
14  Douglas_Kurth   Lockheed
15  Douglas_Kurth   BAASS

networkx_from_pandas_nodes_colored_by_df_column


Although I did not do this here, if you want to add node borders and thicken the node border lines (node edge thickness: linewidths), do the following.

nx.draw(G, pos = nx.nx_pydot.graphviz_layout(G), \
    node_size=1200, node_color=colors, linewidths=2.0, \
    font_size=10, font_weight='bold', with_labels=True)

# Get current axis:
ax = plt.gca()
ax.collections[0].set_edgecolor('r')
# r : red (can also use #FF0000) | b : black (can also use #000000) | ...
plt.show()
Victoria Stuart
  • 4,610
  • 2
  • 44
  • 37
4

After the old Edit:

Can't give too much context as I am not super familiar with bokeh, but looks like you can use a similar approach to what I did initially just instead of passing the "color_map" do your draw function you have to stick your data in here graph_renderer.node_renderer.data_source.data['colors'] Anyway this seems to do the job, Good luck dude.

relation = pd.DataFrame({
                "company":["Google", "Google", "Amazon", "Amazon", "Amazon",
                            "Apple", "Apple", "Apple"],
                "client":["AT&T", "Cisco", "Facebook", "Snap", "Microsoft",
                          "Intel", "IBM", "Visa"]})

G=nx.from_pandas_edgelist(relation, 'company', 'client')
colors = []

for node in G:
    if node in relation["client"].values:
        colors.append("blue")
    else: colors.append("green")

plot = Plot(plot_width=1000, plot_height=800,
            x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1))
plot.title.text = "Company - Client Network"

node_hover_tool = HoverTool(tooltips=[("Company Name", "@index")])
plot.add_tools(node_hover_tool, BoxZoomTool(), ResetTool())

graph_renderer = from_networkx(G, nx.spring_layout, scale=1, center=(0, 0))

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

graph_renderer.edge_renderer.glyph = MultiLine(line_color="red", line_alpha=0.8, line_width=1)
plot.renderers.append(graph_renderer)

output_file("boo.html")
show(plot)


ilamaaa
  • 421
  • 2
  • 8
  • 1
    Thanks very much, that runs and has the desired output. However when the `color_map` is integrated with the following Bokeh code an error occurs. Code: `graph_renderer.node_renderer.glyph= Circle(size=20),fill_color = color_map)`. node_color is not an accepted attribute of `Circle`. Error: `ValueError: expected an element of either String, Dict(Enum('expr', 'field', 'value', 'transform'), Either(String, Instance(Transform), Instance(Expression), Color)) or Color` – moe_95 Mar 26 '19 at 11:41
  • oh sorry, completely ignored the whole bokeh part, not sure i know enough about it to give you answer but I'll a try and edit the answer if I figure it out. – ilamaaa Mar 26 '19 at 11:57
  • Got a bit bored at work and got to learn something new, Edited, should do the job now. – ilamaaa Mar 26 '19 at 18:04