How does one display a bipartite graph in the python networkX package, with the nodes from one class in a column on the left and those from the other class on the right?
I can create a graph and display it like this
B = nx.Graph()
B.add_nodes_from([1,2,3,4], bipartite=0) # Add the node attribute "bipartite"
B.add_nodes_from(['a','b','c'], bipartite=1)
B.add_edges_from([(1,'a'), (1,'b'), (2,'b'), (2,'c'), (3,'c'), (4,'a')])
nx.draw(B)
plt.show()
But I want nodes 1,2,3,4 on the left in a column and the nodes 'a','b','c' in a column on the right, with edges going between them.
You need to set the positions for each node by yourself:
B = nx.Graph()
B.add_nodes_from([1,2,3,4], bipartite=0) # Add the node attribute "bipartite"
B.add_nodes_from(['a','b','c'], bipartite=1)
B.add_edges_from([(1,'a'), (1,'b'), (2,'b'), (2,'c'), (3,'c'), (4,'a')])
# Separate by group
l, r = nx.bipartite.sets(B)
pos = {}
# Update position for node from each group
pos.update((node, (1, index)) for index, node in enumerate(l))
pos.update((node, (2, index)) for index, node in enumerate(r))
nx.draw(B, pos=pos)
plt.show()
Building on #Rikka's answer and newer versions of NetworkX, the following automates (and improves) the positioning of the bipartite network. I've also added labels and different colors to the different partitions of the network.
B = networkx.Graph()
B.add_nodes_from([1,2,3,4], bipartite=0) # Add the node attribute "bipartite"
B.add_nodes_from(['abc','bcd','cef'], bipartite=1)
B.add_edges_from([(1,'abc'), (1,'bcd'), (2,'bcd'), (2,'cef'), (3,'cef'), (4,'abc')])
top = networkx.bipartite.sets(B)[0]
pos = networkx.bipartite_layout(B, top)
networkx.draw(B, pos=pos, with_labels=True, node_color=['green','green','green','green','blue','blue','blue'])
plt.show()
To answer my own question, based on #Rikka above--Here is code to determine the positions for nodes in an arbitrary multipartite graph, given names for the parts.
def position_MultiPartiteGraph( Graph, Parts ):
# Graph is a networkX Graph object, where the nodes have attribute 'agentType' with part name as a value
# Parts is a list of names for the parts (to be shown as columns)
# returns list of dictionaries with keys being networkX Nodes, values being x,y coordinates for plottingxPos = {}
xPos = {}
yPos = {}
for index1, agentType in enumerate(Parts):
xPos[agentType] = index1
yPos[agentType] = 0
pos = {}
for node, attrDict in Graph.nodes(data=True):
agentType = attrDict['agentType']
# print ('node: %s\tagentType: %s' % (node, agentType))
# print ('\t(x,y): (%d,%d)' % (xPos[agentType], yPos[agentType]))
pos[node] = (xPos[agentType], yPos[agentType])
yPos[agentType] += 1
return pos
Now, suppose I define a tripartite graph like this (weights are irrelevant for this example):
TG = nx.Graph()
TG.add_nodes_from([1,2,3,4], agentType='world') # Add the node attribute "bipartite"
TG.add_nodes_from(['a','b','c'], agentType='sender')
TG.add_nodes_from(['A','B','C'], agentType='receiver')
# This is just an easier way to add (and to automatically generate) weighted edges
myEdges = [(1,'a',0.75),
(1,'b',0.25),
(2,'b',0.5),
(2,'c',0.5),
(3,'c',1.0),
(4,'a',1.0),
('a','C',0.10),
('a','A',0.80),
('c','A',1.0),
('b','C',1.0)]
[TG.add_edge(x,y,weight=z) for x,y, z in myEdges]
Then here is how to use it:
nx.draw(TG,pos=position_MultiPartiteGraph(TG, ['world', 'sender', 'receiver']))
plt.show()
I'm not sure how to show the output, but it works for me! Hurray! Thanks #Rikka!
Related
I am creating a graph with the networkx library, using an adjacency list as an entry file. These kinds of lists allow only 2 values per line, source, and destination (as many destinations as you want).
I am filtering the creation of the entry file to add only the UP edge.
- before filtering :
OK node1 node10
OK node10 node99
KO node20 node99
- after filtering :
node1 node10
node10 node99
When I am watching my graphs, I can't see if a node disappeared from the previous one, because I have a lot of them.
Is it possible to parse the status of my edges and display red nodes with a dotted line as an edge for example?
Is it possible to compare my graph to a theoretical one and display the missing link with a different color?
There might be a faster way but here is my logic:
G - final graph(clean, fewer edges)
T - theoretical graph(raw, more edges)
First calculate the difference between edges of the theoretical and final graphs:
diff = T.edges() - G.edges()
Then plot nodes and edges that are within this difference like you pretended:
if e in diff:
for n in e:
if n not in G:
res.add_node(n, color="red")
else:
res.add_node(n, color='#1f78b4') # default networkx color
res.add_edge(*e, style='--')
Nodes and edges that aren't within this difference are drawn normally:
else:
res.add_node(e[0], color = '#1f78b4') # default networkx color
res.add_node(e[1], color = '#1f78b4')
res.add_edge(*e, style='-')
Finally, find the colors of the nodes and the styles of the edges and draw:
colors = [u[1] for u in res.nodes(data="color")]
styles = [res[u][v]['style'] for u,v in res.edges()]
nx.draw(res, with_labels =True, node_color = colors, style = styles)
full code
def draw_theorethical(G,T):
diff = T.edges() - G.edges()
# print("Extra Edges in T", diff, "\nExtra Nodes in T", T.nodes() - G.nodes())
res = nx.Graph()
for e in T.edges():
if e in diff:
for n in e:
if n not in G:
res.add_node(n, color="red")
else:
res.add_node(n, color='#1f78b4') # Node invisible... default networkx color
res.add_edge(*e, style='--')
else:
res.add_node(e[0], color = '#1f78b4')
res.add_node(e[1], color = '#1f78b4')
res.add_edge(*e, style='-')
colors = [u[1] for u in res.nodes(data="color")]
styles = [res[u][v]['style'] for u,v in res.edges()]
nx.draw(res, with_labels =True, node_color = colors, style = styles)
example:
For two graphs, G and T:
T = nx.Graph()
T.add_edge("A","B")
T.add_edge("A","C")
T.add_edge("B","D")
T.add_edge("B","F")
T.add_edge("C","E")
G = nx.Graph(T)
G.remove_edge("C","E")
G.remove_node("E")
draw_theorethical(G,T)
draws:
I have a networks bipartite graph.
this is the code:
G = nx.Graph()
G.add_nodes_from(USsNames, bipartite=0) # Add the node attribute "bipartite"
G.add_nodes_from(TCsNames, bipartite=1)
G.add_weighted_edges_from(compoundArr)
labeldict = {}
# Separate by group
pos = {}
# Update position for node from each group
pos.update({node: [1, index] for index, node in enumerate(USsNames)})
pos.update({node: [2, index] for index, node in enumerate(TCsNames)})
nx.draw(G, pos, node_size=10,with_labels=False)
for p in pos: # raise text positions
pos[p][1] += 0.12
# create the dictionary with the formatted labels
edge_labels = {i[0:2]:'{0:.2f}'.format(i[2]['weight']) for i in G.edges(data=True)}
# add the custom egde labels
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels,font_size=8)
nx.draw_networkx_labels(G, pos,font_size=8)
plt.show()
And the output:
I need to spread the left nodes so they will spread up, and to shorten the right nodes labels (let's say first four chars).
I tried to find a solution but I didn't make it. Thank you.
I reconstructed your case on the generated example data.
Firstly we have this graph.
The size of the left array is far less than the size of the right array so the left array is drawn non-proportionally. To draw it properly, you should modify your position update function:
pos.update({node: [1, index] for index, node in enumerate(USsNames)})
We know that TCsNames is larger than USsNames so we can multiply every USsNames nodes Y-position to their ratio:
pos.update({node: [1, index*(len(TCsNames)/len(USsNames))] for index, node in enumerate(USsNames)})
Now we have this graph:
To crop nodes labels, you should modify your draw_networkx_labels with the labels parameter:
nx.draw_networkx_labels(G, pos, labels=node_labels, font_size=8)
with node_labels equal to:
node_labels = {i: i[:5] for i in G.nodes} (5 is the desired node label length).
And finally we have the graph you need:
So I am using networkX in Python to generate a community map, and now I want to tag each node with it's primary community. Here is the code I use:
parts = community.best_partition(G_fb)
values = [parts.get(node) for node in G_fb.nodes()]
plt.axis("off")
nx.draw_networkx(G_fb, pos = spring_pos, cmap = plt.get_cmap("jet"), node_color = values, node_size = 35, with_labels = False)
Anyone knows how to tag this information?
You can use the set_node_attribute function from networkx. Just get the correct syntax based on your networkx version ( I am using 2.1)
import networkx as nx
import community
G = nx.erdos_renyi_graph(30, 0.05)
#Compute the partition
partition = community.best_partition(G)
nx.set_node_attributes(G, partition, 'best_community')
#Then you access each node to get the property
my_nodes = G.nodes()
my_nodes[0]
#{ 'best_community' : 0 }
Update Based on your comments, you are getting an error because you don't have node numbered 0 in your nodes list, try any node from you data like my_nodes[1229400064]
In a Graph G i have a set of nodes. Some of them have a Attribute Type which can be MASTER or DOC. Others do not have the a Type define:
>>> import networkx as nx
>>> import matplotlib.pyplot as plt
>>> G=nx.Graph()
[...]
>>> G.node['ART1']
{'Type': 'MASTER'}
>>> G.node['ZG1']
{'Type': 'DOC'}
>>> G.node['MG1']
{}
Afterwards I plot the Graph using
>>> nx.draw(G,with_labels = True)
>>> plt.show()
Now i get a graph with red Circles. How can I get e.g.
blue cylces for ART
red squares for DOC
purple cylces for everything undefined
in my plot?
There are various ways to select nodes based on their attributes. Here is how to do it with get_node_attributes and a list comprehension to take the subset. The drawing functions then accept a nodelist argument.
It should be easy enough to extend to a broader set of conditions or modify the appearance of each subset as suits your needs based on this approach
import networkx as nx
# define a graph, some nodes with a "Type" attribute, some without.
G = nx.Graph()
G.add_nodes_from([1,2,3], Type='MASTER')
G.add_nodes_from([4,5], Type='DOC')
G.add_nodes_from([6])
# extract nodes with specific setting of the attribute
master_nodes = [n for (n,ty) in \
nx.get_node_attributes(G,'Type').iteritems() if ty == 'MASTER']
doc_nodes = [n for (n,ty) in \
nx.get_node_attributes(G,'Type').iteritems() if ty == 'DOC']
# and find all the remaining nodes.
other_nodes = list(set(G.nodes()) - set(master_nodes) - set(doc_nodes))
# now draw them in subsets using the `nodelist` arg
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, nodelist=master_nodes, \
node_color='red', node_shape='o')
nx.draw_networkx_nodes(G, pos, nodelist=doc_nodes, \
node_color='blue', node_shape='o')
nx.draw_networkx_nodes(G, pos, nodelist=other_nodes, \
node_color='purple', node_shape='s')
I'm using NetworkX in python. Given any undirected and unweighted graph, I want to loop through all the nodes. With each node, I want to add a random edge and/or delete an existing random edge for that node with probability p. Is there a simple way to do this? Thanks a lot!
Create a new random edge in networkx
Let's set up a test graph:
import networkx as nx
import random
import matplotlib.pyplot as plt
graph = nx.Graph()
graph.add_edges_from([(1,3), (3,5), (2,4)])
nx.draw(graph, with_labels=True)
plt.show()
Now we can pick a random edge from a list of non-edge from the graph. It is not totally clear yet what is the probability you mentioned. Since you add a comment stating that you want to use random.choice I'll stick to that.
def random_edge(graph, del_orig=True):
'''
Create a new random edge and delete one of its current edge if del_orig is True.
:param graph: networkx graph
:param del_orig: bool
:return: networkx graph
'''
edges = list(graph.edges)
nonedges = list(nx.non_edges(graph))
# random edge choice
chosen_edge = random.choice(edges)
chosen_nonedge = random.choice([x for x in nonedges if chosen_edge[0] == x[0]])
if del_orig:
# delete chosen edge
graph.remove_edge(chosen_edge[0], chosen_edge[1])
# add new edge
graph.add_edge(chosen_nonedge[0], chosen_nonedge[1])
return graph
Usage exemple:
new_graph = random_edge(graph, del_orig=True)
nx.draw(new_graph, with_labels=True)
plt.show()
We can still add a probability distribution over the edges in random.choiceif you need to (using numpy.random.choice() for instance).
Given a node i, To add edges without duplication you need to know (1) what edges from i already exist and then compute (2) the set of candidate edges that don't exist from i. For removals, you already defined a method in the comment - which is based simply on (1).
Here is a function that will provide one round of randomised addition and removal, based on list comprehensions
def add_and_remove_edges(G, p_new_connection, p_remove_connection):
'''
for each node,
add a new connection to random other node, with prob p_new_connection,
remove a connection, with prob p_remove_connection
operates on G in-place
'''
new_edges = []
rem_edges = []
for node in G.nodes():
# find the other nodes this one is connected to
connected = [to for (fr, to) in G.edges(node)]
# and find the remainder of nodes, which are candidates for new edges
unconnected = [n for n in G.nodes() if not n in connected]
# probabilistically add a random edge
if len(unconnected): # only try if new edge is possible
if random.random() < p_new_connection:
new = random.choice(unconnected)
G.add_edge(node, new)
print "\tnew edge:\t {} -- {}".format(node, new)
new_edges.append( (node, new) )
# book-keeping, in case both add and remove done in same cycle
unconnected.remove(new)
connected.append(new)
# probabilistically remove a random edge
if len(connected): # only try if an edge exists to remove
if random.random() < p_remove_connection:
remove = random.choice(connected)
G.remove_edge(node, remove)
print "\tedge removed:\t {} -- {}".format(node, remove)
rem_edges.append( (node, remove) )
# book-keeping, in case lists are important later?
connected.remove(remove)
unconnected.append(remove)
return rem_edges, new_edges
To see this function in action:
import networkx as nx
import random
import matplotlib.pyplot as plt
p_new_connection = 0.1
p_remove_connection = 0.1
G = nx.karate_club_graph() # sample graph (undirected, unweighted)
# show original
plt.figure(1); plt.clf()
fig, ax = plt.subplots(2,1, num=1, sharex=True, sharey=True)
pos = nx.spring_layout(G)
nx.draw_networkx(G, pos=pos, ax=ax[0])
# now apply one round of changes
rem_edges, new_edges = add_and_remove_edges(G, p_new_connection, p_remove_connection)
# and draw new version and highlight changes
nx.draw_networkx(G, pos=pos, ax=ax[1])
nx.draw_networkx_edges(G, pos=pos, ax=ax[1], edgelist=new_edges,
edge_color='b', width=4)
# note: to highlight edges that were removed, add them back in;
# This is obviously just for display!
G.add_edges_from(rem_edges)
nx.draw_networkx_edges(G, pos=pos, ax=ax[1], edgelist=rem_edges,
edge_color='r', style='dashed', width=4)
G.remove_edges_from(rem_edges)
plt.show()
And you should see something like this.
Note that you could also do something similar with the adjacency matrix,
A = nx.adjacency_matrix(G).todense() (it's a numpy matrix so operations like A[i,:].nonzero() would be relevant). This might be more efficient if you have extremely large networks.