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.
Related
I'm using the Louvain Algorithm below for community detection using graphs that I insert manually.
I have 2 problems here. The first one is about the color of the nodes. The color of each community of nodes, as you see below, is a bit dark or white and it is not clear as which are the exact communities.
So, which is the way to draw each community of nodes into brighter colors?
And my last question, any ideas to save the results into a new .txt after the community detection is done?
partition = community.best_partition(G)
values = [partition.get(node) for node in G.nodes()]
#drawing
size = float(len(set(partition.values())))
posi = 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, posi, list_nodes, node_size = 25, node_color=str(count/size))
#nx.draw_spring(G, cmap = plt.get_cmap('hsv'), node_color = values, node_size=30, with_labels=False)
nx.draw_networkx_edges(G, posi, alpha=0.5)
plt.show()
You can use the cmap parameter of draw_networkx_nodes, which allows you to specify any matplotlib.colormap. See here or here1 for example.
Minimal working colouring example:
import networkx as nx
import matplotlib.pylab as pl
graph = nx.karate_club_graph()
colors = []
for node in graph:
if graph.nodes[node]["club"] == "Mr. Hi":
colors.append(0)
else:
colors.append(1)
colors[0] = -1
colors[-1] = 2
nx.draw_networkx(graph, node_color=colors, vmin=min(colors), vmax=max(colors), cmap=pl.get_cmap("viridis"))
pl.axis("off")
pl.show()
For the saving of your graph, you can either choose a suitable graph format, such as GML. Then you first need to add the partition as node attribute to your graph:
for node in partition:
G.nodes[node]["cluster"] = partition[node]
# save file
nx.write_gml(G, "path_to_save_file")
# load file
saved_graph = nx.read_gml("path_to_save_file")
and afterwards save the graph together with the partition. Alternatively, you can only save the retrieved partition as json or (unsafe) via pickle.
The goal is to create a networkx graph based on eigenvalue centrality and highlight the top five nodes of highest centrality measure with a different node color.
Below is my current script. It's working fine and everything's just set to one color for now.
#import packages
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import collections
#read data into nx graph
G = nx.from_pandas_dataframe(df, 'col1', 'col2')
#get dictionary of centrality measures and order it
eig = nx.eigenvector_centrality(G)
ordered = collections.OrderedDict(sorted(eig.items(), reverse = True, key=lambda t: t[1]))
#draw graph
nx.draw_spring(G, k=1, node_color = 'skyblue',
node_size = 200, font_size = 6, with_labels = True)
plt.show()
Below is what I'm experimenting with for the node coloring. I'm attempting to append the first five ordered dictionary key names to the color_map, setting them to a different color from the rest. Please let me know if you have any suggestions here or if another method would simpler. If possible, I'd prefer to stick to the packages I'm using.
#adjust color of top three
color_map = []
for key, value in ordered:
if key < 5:
color_map.append('blue')
else: color_map.append('green')
Figured it out. There's no need to create a separate OrderedDict. Applying the key=eig.get argument to sorted() allows you to sort a dictionary by value (the biggest obstacle for this problem). Then I can just filter this and apply it to cmap.
Not a bad question, but I am pretty sure its a duplicate...Either way, I'll try to answer it. You will have to forgive me for being vague because I can't run your code and I've not tried to write an example of your problem. I will update my answer when I can run an example of your code later. However, for now I believe changing the node colour is similar to changing the edge colour. Going from that, you need to focus on the this line of your code to change the node colour;
#draw graph
nx.draw_spring(G, k=1, node_color = 'skyblue',
node_size = 200, font_size = 6, with_labels = True)
and this is an example of how you could do this;
# depending on how your graph is being read from the pandas-
# dataframe this should pull out the correct value to associate-
# the colour with
nodes = G.nodes()
# Just a list to hold all the node colours
node_colours = []
for node in nodes:
if node < 5:
node_colours.append('blue')
else:
node_colours.append('green')
#draw graph, substitute the single colour with ur list of colours
nx.draw_spring(G, k=1, node_color = node_colours,
node_size = 200, font_size = 6, with_labels = True)
I hope this helps! I will get back to it later when I test an example of this on my own machine.
-Edit-
Since the OP has answered his own question, I will not elaborate further with my example. However, if anyone is interested in my example (but if it unfortunately throws any error) and wants me to fix it and expand on it, just leave me a comment.
Tiz
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!
I've been studying the other networkx plotting posts, but I've been having a hard time adapting them to my problem.
1) How do I create subplots with network graphs, without a PREDEFINED number of objects to plot? The function grabs this dynamically.
2) Is there an easy way to filter the network graph by limiting only those edges with over a weight of 2, for instance? or do I have to edit the network object itself to do so?
UPDATE #2: I figured out a way to filter by degree (see below). I'm wondering more generally if there are better ways to make my network data more understandable?
nol comes in the format [ [Year, networkobject], [Year, networkobject]]
def standardgraph_multiyear(nol, minimumdegree):
"""
Plots multiple graphs based on year
nol = takes in a LIST of [year, network object]
minimum = takes in a digit to filter nodes by degree
"""
#Each iteration prints a new subplot
numrows = len(nol)
fig = plt.figure(figsize=(10,60))
for i, val in enumerate(nol):
gloc = numrows,1,i+1
plt.subplot(numrows, 1, i+1)
if minimumdegree > 0:
outdeg = val[1].degree()
to_keep = [n for n in outdeg if outdeg[n] > minimumdegree]
mingraph = val[1].subgraph(to_keep)
pos = nx.spring_layout(mingraph, iterations=200)
nx.draw(mingraph, pos, font_size=8, with_labels=True)
nx.draw_networkx_edges(mingraph, pos, alpha=.2)
nx.draw_networkx_nodes(mingraph, pos, node_size=60, font_size =8, labels=True)
nx.draw_networkx_labels(mingraph, pos, font_color='k', font_size=8)
plt.title("Year {0}".format(val[0]), loc = 'center', fontsize=20)
if minimumdegree == 0:
outdeg = val[1].degree()
to_keep = [n for n in outdeg if outdeg[n] > minimumdegree]
mingraph = val[1].subgraph(to_keep)
pos = nx.spring_layout(mingraph, iterations=200)
nx.draw(mingraph, pos, font_size=8, with_labels=True)
nx.draw_networkx_edges(mingraph, pos, alpha=.2)
nx.draw_networkx_nodes(mingraph, pos, node_size=60, font_size =8, labels=True)
nx.draw_networkx_labels(mingraph, pos, font_color='k', font_size=8)
plt.title("Year {0}".format(val[0]), loc = 'center', fontsize=20)
return
fig.savefig('out.png', dpi=100)
Your out of range error likely comes from the call to plt.subplot(221+i), since you don't seem to limit i to be <4; thus matplotlib will not know what subplot you intend to refer to?
(You also seem to have some conflicting code assembling the plots: a call to plt.subplots(1,1) and a later which requests a 2x2 grid).
In a different question I used the more basic plt.subplot(xxx) syntax to generate multiple subplots (following the four grids example from networkx). But you can also do it as shown below, setting the ax= keyword argument to an already existing set of axes. Note the call to sca() before rendering to each axis, which I needed to get this to work.
I've also shown one way to filter the edges that are shown below, and it does not require modifying the underlying graph: instead, you construct the edge lineweights you want based on the data from your graph, and use that as argument to draw_networkx_edges.
Edit (re updated question): the example code now includes a more explicit illustration of how to handle an unknown number of networks.
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
n = 15; m = 40 # graph size
L = np.random.choice(xrange(n), 2*m) # select some edge destinations
weights = 0.5 + 5 * np.random.rand(m) # give each edge a weight
G = nx.Graph() # create a graph object
G.add_nodes_from(xrange(n)) # add n nodes to it
for i, (fr, to) in enumerate(zip(L[1::2], L[::2])):
G.add_edge(fr, to, weight=weights[i]) # add each edge
# use one of the edge properties to control line thickness
edgewidth = [ d['weight'] for (u,v,d) in G.edges(data=True)]
# and create a filtered version (still need an entry for each edge)
w_threshold = 2
edgewidth_filtered = [ d['weight'] if d['weight'] > w_threshold else 0
for (u,v,d) in G.edges(data=True)]
# alt. filtering - all edges that meet some criterion are displayed uniformly
binary_filtered_edges = [ 1 if d['weight'] > w_threshold else 0
for (u,v,d) in G.edges(data=True)]
titles = [ 'edges drawn with lineweight=1', 'edge width from edge weight',
'edge width from edge weight, only strong edges',
'strong edges shown as lineweight=1', ]
edge_display_params = [ np.ones(len(edgewidth),), edgewidth,
edgewidth_filtered, binary_filtered_edges,]
# to illustrate drawing an unknown number of graphs, add some repeats repeats
n_extra = np.random.randint(0, 5)
indices = range(len(edge_display_params)) * 3
indices = indices[len(edge_display_params) + n_extra:]
# layout
pos = nx.spring_layout(G, iterations=50)
pos = nx.circular_layout(G)
#pos = nx.random_layout(G)
# rendering
fig = plt.figure(1); plt.clf()
# compute a grid size that will fit all graphs on it (couple blanks likely)
nr = int(np.ceil(np.sqrt(len(indices))))
fig, ax = plt.subplots(nr, nr, num=1)
for i, j in enumerate(indices):
# dereference index into valid data (needed here since some repeated rather
# than creating more, to illustrate handling unknown amount of data)
k = indices[j]
widths = edge_display_params[k]
# compute index for the subplot, and set this subplot as current
ix = np.unravel_index(i, ax.shape)
plt.sca(ax[ix])
# draw all nodes homogeneously, and edge weights as filtered
nx.draw_networkx_nodes(G, pos, ax=ax[ix])
nx.draw_networkx_edges(G, pos, width=widths, ax=ax[ix],)
ax[ix].set_title(titles[k], fontsize=10)
ax[ix].set_axis_off()
plt.show()
This example uses the same input graph four times over but obviously you could apply a single filter to different graphs (by filtering within the plotting loop) instead of applying different filters.
Below shows one run that created an extra 4 graphs, and so we have one unused pane:
I imported my Facebook data onto my computer in the form of a .json file. The data is in the format:
{"nodes":[{"name":"Alan"},{"name":"Bob"}],"links":[{"source":0,"target:1"}]}
Then, I use this function:
def parse_graph(filename):
"""
Returns networkx graph object of facebook
social network in json format
"""
G = nx.Graph()
json_data=open(filename)
data = json.load(json_data)
# The nodes represent the names of the respective people
# See networkx documentation for information on add_* functions
G.add_nodes_from([n['name'] for n in data['nodes']])
G.add_edges_from([(data['nodes'][e['source']]['name'],data['nodes'][e['target']]['name']) for e in data['links']])
json_data.close()
return G
to enable this .json file to be used a graph on NetworkX. If I find the degree of the nodes, the only method I know how to use is:
degree = nx.degree(p)
Where p is the graph of all my friends. Now, I want to plot the graph such that the size of the node is the same as the degree of that node. How do I do this?
Using:
nx.draw(G,node_size=degree)
didn't work and I can't think of another method.
Update for those using networkx 2.x
The API has changed from v1.x to v2.x. networkx.degree no longer returns a dict but a DegreeView Object as per the documentation.
There is a guide for migrating from 1.x to 2.x here.
In this case it basically boils down to using dict(g.degree) instead of d = nx.degree(g).
The updated code looks like this:
import networkx as nx
import matplotlib.pyplot as plt
g = nx.Graph()
g.add_edges_from([(1,2), (2,3), (2,4), (3,4)])
d = dict(g.degree)
nx.draw(g, nodelist=d.keys(), node_size=[v * 100 for v in d.values()])
plt.show()
nx.degree(p) returns a dict while the node_size keywod argument needs a scalar or an array of sizes. You can use the dict nx.degree returns like this:
import networkx as nx
import matplotlib.pyplot as plt
g = nx.Graph()
g.add_edges_from([(1,2), (2,3), (2,4), (3,4)])
d = nx.degree(g)
nx.draw(g, nodelist=d.keys(), node_size=[v * 100 for v in d.values()])
plt.show()
#miles82 provided a great answer. However, if you've already added the nodes to your graph using something like G.add_nodes_from(nodes), then I found that d = nx.degree(G) may not return the degrees in the same order as your nodes.
Building off the previous answer, you can modify the solution slightly to ensure the degrees are in the correct order:
d = nx.degree(G)
d = [(d[node]+1) * 20 for node in G.nodes()]
Note the d[node]+1, which will be sure that nodes of degree zero are added to the chart.
other method if you still get 'DiDegreeView' object has no attribute 'keys'
1)you can first get the degree of each node as a list of tuples
2)build a node list from the first value of tuple and degree list from the second value of tuple.
3)finally draw the network with the node list you've created and degree list you've created
here's the code:
list_degree=list(G.degree()) #this will return a list of tuples each tuple is(node,deg)
nodes , degree = map(list, zip(*list_degree)) #build a node list and corresponding degree list
plt.figure(figsize=(20,10))
nx.draw(G, nodelist=nodes, node_size=[(v * 5)+1 for v in degree])
plt.show() #ploting the graph