How to draw half - filled nodes in a networkx graph? - python-3.x

I am drawing a graph using the NetworkX library where I want semi-circular nodes.
The node_shape attribute in nx.draw_networkx_nodes refers to the matplotlib.scatter marker specifications. But, there is no option of a half-filled circle. Moreover matplotlib.lines has the attribute fillStyles, but I am confused about how I can implement in the code.
nx.draw_networkx_nodes(G,pos,
node_list = nodes.keys(),
node_size = [n for n in nodes.values()],
node_color = '#78CCF0',
node_shape = '.',
alpha = 0.77)
Here's a quick look: https://imgur.com/a/wsyQls3

import networkx as nx
G=nx.dodecahedral_graph()
nodes=nx.draw_networkx_nodes(G,pos=nx.spring_layout(G),
node_shape=matplotlib.markers.MarkerStyle(marker='o',
fillstyle='top'))

Related

Renaming a Graph in Networkx

I am new on Python and I am trying to learn Networkx ( https://networkx.github.io/ )
I am trying to run a basic a code:
import networkx as nx
import matplotlib.pyplot as plt
G=nx.Graph()
G.add_node(1)
G.add_node(2)
G.add_node(3)
G.add_node(4)
G.add_node(5)
G.add_node(6)
G.add_node(7)
G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(3,4)
G.add_edge(4,5)
G.add_edge(5,6)
G.add_edge(6,7)
G.add_edge(7,1)
G.add_edge(1,3)
G.add_edge(1,4)
G.add_edge(1,5)
G.add_edge(1,6)
G.add_edge(1,7)
G.add_edge(2,4)
G.add_edge(2,5)
G.add_edge(2,6)
G.add_edge(2,7)
G.add_edge(3,5)
G.add_edge(3,6)
G.add_edge(3,7)
G.add_edge(4,6)
G.add_edge(4,7)
G.add_edge(5,7)
nx.draw(G)
plt.savefig("graph1.png")
plt.show()
and this is the graph generated:
The problem comes when trying to add names to the nodes. I am running the next code:
import networkx as nx
import matplotlib.pyplot as plt
G=nx.Graph()
G.add_node(1)
G.add_node(2)
G.add_node(3)
G.add_node(4)
G.add_node(5)
G.add_node(6)
G.add_node(7)
G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(3,4)
G.add_edge(4,5)
G.add_edge(5,6)
G.add_edge(6,7)
G.add_edge(7,1)
G.add_edge(1,3)
G.add_edge(1,4)
G.add_edge(1,5)
G.add_edge(1,6)
G.add_edge(1,7)
G.add_edge(2,4)
G.add_edge(2,5)
G.add_edge(2,6)
G.add_edge(2,7)
G.add_edge(3,5)
G.add_edge(3,6)
G.add_edge(3,7)
G.add_edge(4,6)
G.add_edge(4,7)
G.add_edge(5,7)
names = {1:"Central",2:"South",3:"North",4:"East",5:"West",6:"Up",7:"Down"}
H=nx.relabel_nodes(G,names)
nx.draw(H)
plt.savefig("graph1.png")
plt.show()
and the resulted graph is this one:
How can I add names to the nodes? I am using python 3.8 and Networkx 2.4
You can either use nx.draw(H, with_labels=True),
Or nx.draw_networkx(H), which has with_labels=True as default.
documentation of nx.draw:
draw(G, pos=None, ax=None, **kwds)
[...]
kwds (optional keywords) – See networkx.draw_networkx() for a description of optional keywords.
documentation of nx.draw_networkx
draw_networkx(G, pos=None, arrows=True, with_labels=True, **kwds)
[...]
with_labels (bool, optional (default=True)) – Set to True to draw labels on the nodes.
edit:
draw edges with different colors:
check out the function nx.draw_networkx_edges
relevant attributes:
edge_color (color string, or array of floats) – Edge color. Can be a single color
format string (default=’r’), or a sequence of colors with the same length as edgelist. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters.
style (string) – Edge line style (default=’solid’) (solid|dashed|dotted,dashdot)
alpha (float) – The edge transparency (default=1.0)
cmap (edge) – Colormap for mapping intensities of edges (default=None)
edge_vmin,edge_vmax (floats) – Minimum and maximum for edge colormap scaling (default=None)
So, you can make a list of strings:
colors = ['red'] * len(G.edges()
pos = nx.spring(layout(G))
nx.draw_networkx_nodes(G, pos=pos)
nx.draw_networkx_edges(G, pos=pos, edge_color=colors)
or use numbers and a colormap:
colors = [np.random.rand() for e in G.edges()]
pos = nx.spring(layout(G))
nx.draw_networkx_nodes(G, pos=pos)
nx.draw_networkx_edges(G, pos=pos, edge_color=colors, cmap='viridis')

louvain community detection in complete weighted networks returns only 1 partition

Referring to : https://stackoverflow.com/a/44907357/305883
I am using python-louvain implementation to detect community in complete weighted graph.
But I only get one partition, containing all nodes.
Code:
import community # this is pip install python-louvain
import networkx as nx
import matplotlib.pyplot as plt
# Replace this with your networkx graph loading depending on your format !
# using graph g as a completed graph, weights between 0 and 1
#first compute the best partition
partition = community.best_partition(g)
#drawing
size = float(len(set(partition.values())))
pos = nx.spring_layout(g)
count = 0.
for com in set(partition.values()) :
count = count + 1.
list_nodes = [nodes for nodes in partition.keys() if partition[nodes] == com]
nx.draw_networkx_nodes(g, pos, list_nodes, node_size = 20, node_color = str(count / size))
nx.draw_networkx_edges(g, pos, alpha=0.1)
plt.show()
I would like to extract communities from a complete weighted network.
I also tried girvan_newman (https://networkx.github.io/documentation/networkx-2.0/reference/algorithms/generated/networkx.algorithms.community.centrality.girvan_newman.html) but could only detect 2 communities out of a complete graph of 200 nodes (with 198 and 2 nodes).
Is Louvain working correctly to detect communities in complete graph?
Better suggestions?
It is possible that the used model selection for this case returns a single block with all nodes, which means that there is not enough statistical evidence for more blocks.
You could try Peixotos graph-tool package, which has an implementation of weighted stochastic block model.
If you have a weighted network you need to use the weight='weight' argument:
import networkx as nx
import community
import numpy as np
np.random.seed(0)
W = np.random.rand(15,15)
np.fill_diagonal(W,0.0)
G = nx.from_numpy_array(W)
louvain_partition = community.best_partition(G, weight='weight')
modularity2 = community.modularity(louvain_partition, G, weight='weight')
print("The modularity Q based on networkx is {}".format(modularity2))
The modularity Q based on networkx is 0.0849022950503318

Color nodes by networkx

I am generating a network topology diagram through the data in a csv file where s0..s2 and c1..c3 are nodes of the diagram.
network.csv:
source,port,destination
s1,1,c3
s2,1,c1
s0,1,c2
s1,2,s2
s2,2,s0
I need to make all the source to be blue and destinations to be green.
How can I do it without overriding the source nodes?
Following is a working solution:
import csv
import networkx as nx
from matplotlib import pyplot as plt
with open('../resources/network.csv') as csvfile:
reader = csv.DictReader(csvfile)
edges = {(row['source'], row['destination']) for row in reader }
print(edges) # {('s1', 'c3'), ('s1', 's2'), ('s0', 'c2'), ('s2', 's0'), ('s2', 'c1')}
G = nx.DiGraph()
source_nodes = set([edge[0] for edge in edges])
G.add_edges_from(edges)
for n in G.nodes():
G.nodes[n]['color'] = 'b' if n in source_nodes else 'g'
pos = nx.spring_layout(G)
colors = [node[1]['color'] for node in G.nodes(data=True)]
nx.draw_networkx(G, pos, with_labels=True, node_color=colors)
plt.show()
We first read the csv to an edge list, which is later used for the construction of G. For well defining the colors we set each source node with blue and the rest of the nodes as green (i.e., all destination nodes that also not source nodes).
We also use nx.draw_networkx to get a more compact implementation for drawing the graph.
The result should be something like:

Draw Graphs proportional to weight of edges using networkx

I'm having a Graph to display, but it should be displayed where edges are proportional to the weight. I used networkx library to draw the graph but it draw nodes randomly.
Here is the part of my code to display graph:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
# Added nodes and Edges
pos = nx.spring_layout(G)
nx.draw(graph, pos=pos, nodelist=nodes, with_labels=True)
plt.show()
How can I create a graph where the edge length is weighted?
If it helps I'm also open to use a different library than matplotlib.
The graphviz force directed algorithm outputs what I want. (But I am not sure why it is different than the spring layout from networkx)
import networkx as nx
import pylab as plt
from networkx.drawing.nx_agraph import graphviz_layout
G = nx.Graph()
G.add_node(1)
G.add_node(2)
G.add_node(3)
G.add_node(4)
G.add_edge(1,2, len=4.5)
G.add_edge(2,3, len=2.5)
G.add_edge(3,4, len=7)
G.add_edge(4,1, len=10)
G.add_edge(3,1, len=4.5)
G.add_edge(4,2, len=9)
pos=graphviz_layout(G)
nx.draw(G, pos, node_size=1600, node_color=range(len(G)), with_labels=True, cmap=plt.cm.Dark2)
plt.show()

Color of the node of tree with graphviz using class_names

Expanding on a prior question:
Changing colors for decision tree plot created using export graphviz
How would I color the nodes of the tree bases on the dominant class (species of iris), instead of a binary distinction? This should require a combination of the iris.target_names, the string describing the class, and iris.target, the class.
import pydotplus
from sklearn.datasets import load_iris
from sklearn import tree
import collections
clf = tree.DecisionTreeClassifier(random_state=42)
iris = load_iris()
clf = clf.fit(iris.data, iris.target)
dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=iris.feature_names,
class_names=iris.target_names,
filled=True, rounded=True,
special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
nodes = graph.get_node_list()
edges = graph.get_edge_list()
colors = ('brown', 'forestgreen')
edges = collections.defaultdict(list)
for edge in graph.get_edge_list():
edges[edge.get_source()].append(int(edge.get_destination()))
for edge in edges:
edges[edge].sort()
for i in range(2):
dest = graph.get_node(str(edges[edge][i]))[0]
dest.set_fillcolor(colors[i])
graph.write_png('tree.png')
The code from the example looks so familiar and is therefore easy to modify :)
For each node Graphviz tells us how many samples from each group we have, i.e. if it is a mixed population or the tree came to a decision. We can extract this info and use to get a color.
values = [int(ii) for ii in node.get_label().split('value = [')[1].split(']')[0].split(',')]
Alternatively you can map the GraphViz nodes back to the sklearn nodes:
values = clf.tree_.value[int(node.get_name())][0]
We only have 3 classes, so each one gets its own color (red, green, blue), mixed populations get mixed colors according to their distribution.
values = [int(255 * v / sum(values)) for v in values]
color = '#{:02x}{:02x}{:02x}'.format(values[0], values[1], values[2])
We can now see the separation nicely, the greener it gets the more of the 2nd class we have, same for blue and the 3rd class.
import pydotplus
from sklearn.datasets import load_iris
from sklearn import tree
clf = tree.DecisionTreeClassifier(random_state=42)
iris = load_iris()
clf = clf.fit(iris.data, iris.target)
dot_data = tree.export_graphviz(clf,
feature_names=iris.feature_names,
out_file=None,
filled=True,
rounded=True,
special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
nodes = graph.get_node_list()
for node in nodes:
if node.get_label():
values = [int(ii) for ii in node.get_label().split('value = [')[1].split(']')[0].split(',')]
values = [int(255 * v / sum(values)) for v in values]
color = '#{:02x}{:02x}{:02x}'.format(values[0], values[1], values[2])
node.set_fillcolor(color)
graph.write_png('colored_tree.png')
A general solution for more than 3 classes which colors only the final nodes .
colors = ('lightblue', 'lightyellow', 'forestgreen', 'lightred', 'white')
for node in nodes:
if node.get_name() not in ('node', 'edge'):
values = clf.tree_.value[int(node.get_name())][0]
#color only nodes where only one class is present
if max(values) == sum(values):
node.set_fillcolor(colors[numpy.argmax(values)])
#mixed nodes get the default color
else:
node.set_fillcolor(colors[-1])
Great answers guys. Just to add to #Maximilian Peters's answer. One other thing that one can do identify leaf nodes for specific coloration is to check on the split_criteria(threshold) values. Since leaf nodes don't have child nodes, hence the absence of split criteria as well.
https://github.com/scikit-learn/scikit-learn/blob/a24c8b464d094d2c468a16ea9f8bf8d42d949f84/sklearn/tree/_tree.pyx
TREE_UNDEFINED = -2
thresholds = clf.tree_.threshold
for node in nodes:
if node.get_name() not in ('node', 'edge'):
value = clf.tree_.value[int(node.get_name())][0]
# color only nodes where only one class is present or if it is a leaf
# node
if max(values) == sum(values) or
thresholds[int(node.get_name())] == TREE_UNDEFINED:
node.set_fillcolor(colors[numpy.argmax(value)])
# mixed nodes get the default color
else:
node.set_fillcolor(colors[-1])
Not completely related to the question, but adding some more info in-case it is helpful to others.
Continuing on this idea of understanding the decision stumps of a tree-based classifier, Skater has added support to summarize all forms of tree-based models using tree surrogates. Check out the examples here.
https://github.com/datascienceinc/Skater/blob/master/examples/rule_list_notebooks/explanation_using_tree_surrogate.ipynb

Resources