2
votes

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:

enter image description here

2

2 Answers

5
votes

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).

karateeee, hiyyaaagh

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.

karat2

1
votes

setting attribute 'style' to 'filled' is not work. change it to 'wedged' will be fine.

karate_agr.node_attr['style'] = 'wedged'