I want to color nodes and edges of Karate club graph. But some of nodes have more than one color. Is there any way to color a node with more than one color in python (especially with networkx)? I need something like this:
2 Answers
This can be done but it will probably require a lot of work to obtain the exact result you want. You could start with networkx and pygraphviz like this:
import networkx as nx
karate = nx.generators.social.karate_club_graph()
karate_agr = nx.nx_agraph.to_agraph(karate)
karate_agr.node_attr['style'] = 'filled'
karate_agr.node_attr['shape'] = 'circle'
karate_agr.node_attr['gradientangle'] = 90
for i in karate_agr.nodes():
n = karate_agr.get_node(i)
n.attr['fillcolor'] = 'green;0.5:yellow'
karate_agr.draw('karate.png',prog='dot')
Pygraphviz makes use of graphviz which has really a lot of options. Most of them can be set either for individual nodes (or edges) or globally for all of them like in the example above. It is all well explained in the graphviz documentation.
The above snippet only shows how to make the nodes filled half with one color and half with the other. See the result below (not very beautiful, I know).
EDIT
Hmm, so this kinda grew on me, and I really wanted to make something more similar to what you posted. This is what I came up with:
# coding: utf-8
import networkx as nx
import itertools
from collections import Counter
def edge_in_com(nodes, graph):
edges = []
for (i, j) in itertools.combinations(nodes, 2):
if (i, j) in graph.edges():
edges.append((i, j))
return edges
karate = nx.generators.social.karate_club_graph()
karate_agr = nx.nx_agraph.to_agraph(karate)
karate_agr.graph_attr['dpi'] = 180
karate_agr.edge_attr.update(
dir='both', arrowhead='inv', arrowtail='inv', penwidth=2.0)
karate_agr.node_attr.update(
style='filled',
fontcolor='white',
shape='circle',
color='transparent',
gradientangle=90)
colors = ['grey', 'pink', 'blue', 'purple']
communities = list(nx.community.asyn_fluidc(karate, 4))
most_edges = []
for n, com in enumerate(communities):
edges = edge_in_com(com, karate)
most_edges.extend(edges)
for edge in edges:
e = karate_agr.get_edge(*edge)
e.attr['color'] = colors[n]
for node in com:
node = karate_agr.get_node(node)
node.attr['fillcolor'] = colors[n]
other = [e for e in karate.edges() if e not in most_edges]
for edge in other:
gn = karate_agr.get_node
color = gn(edge[0]).attr['fillcolor']
karate_agr.get_edge(*edge).attr['color'] = color
for n in karate_agr.nodes():
cls = [e.attr['color'] for e in karate_agr.in_edges(n)]
cls2 = [e.attr['color'] for e in karate_agr.out_edges(n)]
cls = set(cls + cls2)
if len(cls) > 1:
# if n.attr['fillcolor'] != cls[0]:
color1 = cls.pop()
color2 = cls.pop()
color_mix = ''.join([color1, ';', '0.5:', color2])
n.attr['fillcolor'] = color_mix
karate_agr.draw('karate.png', prog='neato')
The program definitely can be improved, and I'm still not very happy with the results but maybe you'll find it helpful.