I want to generate graph form an adjacency list but I am not happy with how the nodes are positioned. I want them to be positioned according to a pre-defined scheme which resembles a regular grid with arbitrary coordinates x and y, while still maintaining the scale-free features. Let me give an example: A barabasi-albert network with node 1 located at x_1 = 0.6 and y_1 = 0.5, node 2 located at x_2 = -0.5 and y_2 = 1 ... and so on. I have a list of coordinates of each node.
Have a look at the pos parameter of draw_networkx_XXX functions here.
It can be used like this:
import networkx as nx
import matplotlib.pyplot as plt
from random import randint,seed;
seed(1)
nodes = list(range(5))
edges = [ (nodes[i-1],nodes[i]) for i in range(1,len(nodes)) ]
# here we can set the coordinates to our liking
positions = { node:(randint(0,9),randint(0,9)) for node in nodes }
G=nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
nx.draw_networkx(G,pos=positions, with_labels=False, node_size=100)
plt.show()
[Edit]
Here's how we can build the graph from the adjancency list and assign real values to node positions.
import networkx as nx
import matplotlib.pyplot as plt
from random import randint,seed
from pprint import pprint
seed(0)
edges = [ (randint(0,5),randint(0,5)) for i in range(5) ]
G=nx.Graph()
# nodes added automatically with add_edges_from
G.add_edges_from(edges)
# here we can set positions to our liking
positions = { node: (round((5-randint(0,9))/7.0,2)
, round((5-randint(0,9))/7.0,2)) for node in G.nodes }
pprint({ "edges:": edges, "nodes:":list(G.nodes), "positions:":positions }, width=100)
nx.draw_networkx(G, pos = positions, with_labels=False, node_size=100)
plt.show()
Using positions from a csv file is straightforward.
The pos parameter is really supposed to be a dict with node names as keys (I edited the first snippet to reflect that).
So, if we have a csv file with node names and positions, we just build a dict from it and supply the dict for pos.
Related
I want to visualise some points on a graph, the points move along the link, but they are not nodes. Currently I have added some point location, but can not display them on the figure.
This is the code
# -- coding: utf-8 --
import networkx as nx
import matplotlib.pyplot as plt
import itertools
import math
#from mesa.space import NetworkGrid
#from mesa import Agent, Model
#from mesa.time import RandomActivation
#from mesa.datacollection import DataCollector
#from mesa.space import NetworkGrid
#%%Build a graph
G=nx.Graph()
G.add_node("GPs")
G.add_node("AcuteCares")
G.add_node("Waitlists")
G.add_node("newPatients")
G.add_node("Preventabledeaths")
G.add_node("ReviewPatients")
G.add_node("DeathPools")
G.add_node("DNAPool1s")
G.add_node("DNAPool2s")
G.add_node("UntreatedPopulations")
G.add_node("SAPops")
labeldict = {}
labeldict["GPs"] = "GP"
labeldict["AcuteCares"] = "Acute Care"
labeldict["Waitlists"] = "Waitlist"
labeldict["newPatients"] = "New Patients"
labeldict["Preventabledeaths"] = "Preventable Deaths"
labeldict["ReviewPatients"] = "Review Patients"
labeldict["DeathPools"] = "Natural Deaths"
labeldict["DNAPool1s"] = "First DNA"
labeldict["DNAPool2s"] = "Second DNA"
labeldict["UntreatedPopulations"] = "Untreated Population"
labeldict["SAPops"] = "General Population"
G.node["Preventabledeaths"]['pos']=(0,6)
G.node["ReviewPatients"]['pos']=(-3,5)
G.node["UntreatedPopulations"]['pos']=(3,5)
G.node["DNAPool2s"]['pos']=(-3,3)
G.node["Waitlists"]['pos']=(3,3)
G.node["AcuteCares"]['pos']=(-5,0)
G.node["DNAPool1s"]['pos']=(5,0)
G.node["GPs"]['pos']=(-3,-5)
G.node["DeathPools"]['pos']=(3,-5)
G.node["SAPops"]['pos']=(-3,-3)
G.node["newPatients"]['pos']=(3,-3)
edges=itertools.permutations(G.nodes(),2)
G.add_edges_from(edges)
pos=nx.get_node_attributes(G,'pos')
nx.draw(G,pos,labels=labeldict, with_labels = True)
plt.show()
#grid = NetworkGrid(G)
# %%
def arclen(edge):
"""
calculate the length of an edge. The format of edge is like: ('UntreatedPopulations', 'SAPops')
"""
dist_edge = math.sqrt((G.node[edge[0]]['pos'][0] - G.node[edge[1]]['pos'][0])**2 + (G.node[edge[0]]['pos'][1] - G.node[edge[1]]['pos'][1])**2)
return dist_edge
def patientcor(speed,step,edge):
"""get the coordinate of point along the edge, speed is the moving speed per step,
time is the number of steps, edge is the specific edge
"""
x=G.node[edge[0]]['pos'][0] + speed*step/arclen(edge) *(G.node[edge[1]]['pos'][0] -G.node[edge[0]]['pos'][0])
y=G.node[edge[0]]['pos'][1] + speed*step/arclen(edge) *(G.node[edge[1]]['pos'][1] -G.node[edge[0]]['pos'][1])
return (x,y)
#%% Visualise the graph, set the speed at 0.2, time is 0,1,2
edge=("SAPops","GPs")
for t in range(3):
pos[t]=patientcor(0.2, t,edge) #add the location of point on the link per step to the dict
nx.draw(G,pos, labels=labeldict,with_labels = True) #visualise pos dict along with the graph, but the additional points other than nodes do not appear on the figure
plt.show()
The graph figure only displays the nodes, but not the points that move along the edges:
The nx.draw command will only plot those nodes that are in the graph. If your dictionary pos provides locations of other points, it will silently ignore them. I believe this is the appropriate behavior and I can think of lots of times where my coding would be much more difficult if it would also plot other points that appeared in my pos dictionary.
For what you want, simply create a new list of the points you want to plot (or in your example it looks like just a single point). Then use matplotlib's scatter command.
#stuff skipped here
edge=("SAPops","GPs")
for t in range(3):
mypoint = patientcor(0.2, t,edge)
nx.draw(G,pos, labels=labeldict,with_labels = True) #visualise pos dict along with the graph, but the additional points other than nodes do not appear on the figure
plt.scatter([mypoint[0]], [mypoint[1]])
plt.show()
You'll probably want to play with the node sizes and specific locations of these points.
How can I draw a graph with it's communities using python networkx like this image :
image url
The documentation for networkx.draw_networkx_nodes and networkx.draw_networkx_edges explains how to set the node and edge colors. The patches bounding the communities can be made by finding the positions of the nodes for each community and then drawing a patch (e.g. matplotlib.patches.Circle) that contains all positions (and then some).
The hard bit is the graph layout / setting the node positions.
AFAIK, there is no routine in networkx to achieve the desired graph layout "out of the box". What you want to do is the following:
Position the communities with respect to each other: create a new, weighted graph, where each node corresponds to a community, and the weights correspond to the number of edges between communities. Get a decent layout with your favourite graph layout algorithm (e.g.spring_layout).
Position the nodes within each community: for each community, create a new graph. Find a layout for the subgraph.
Combine node positions in 1) and 3). E.g. scale community positions calculated in 1) by a factor of 10; add those values to the positions of all nodes (as computed in 2)) within that community.
I have been wanting to implement this for a while. I might do it later today or over the weekend.
EDIT:
Voila. Now you just need to draw your favourite patch around (behind) the nodes.
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def community_layout(g, partition):
"""
Compute the layout for a modular graph.
Arguments:
----------
g -- networkx.Graph or networkx.DiGraph instance
graph to plot
partition -- dict mapping int node -> int community
graph partitions
Returns:
--------
pos -- dict mapping int node -> (float x, float y)
node positions
"""
pos_communities = _position_communities(g, partition, scale=3.)
pos_nodes = _position_nodes(g, partition, scale=1.)
# combine positions
pos = dict()
for node in g.nodes():
pos[node] = pos_communities[node] + pos_nodes[node]
return pos
def _position_communities(g, partition, **kwargs):
# create a weighted graph, in which each node corresponds to a community,
# and each edge weight to the number of edges between communities
between_community_edges = _find_between_community_edges(g, partition)
communities = set(partition.values())
hypergraph = nx.DiGraph()
hypergraph.add_nodes_from(communities)
for (ci, cj), edges in between_community_edges.items():
hypergraph.add_edge(ci, cj, weight=len(edges))
# find layout for communities
pos_communities = nx.spring_layout(hypergraph, **kwargs)
# set node positions to position of community
pos = dict()
for node, community in partition.items():
pos[node] = pos_communities[community]
return pos
def _find_between_community_edges(g, partition):
edges = dict()
for (ni, nj) in g.edges():
ci = partition[ni]
cj = partition[nj]
if ci != cj:
try:
edges[(ci, cj)] += [(ni, nj)]
except KeyError:
edges[(ci, cj)] = [(ni, nj)]
return edges
def _position_nodes(g, partition, **kwargs):
"""
Positions nodes within communities.
"""
communities = dict()
for node, community in partition.items():
try:
communities[community] += [node]
except KeyError:
communities[community] = [node]
pos = dict()
for ci, nodes in communities.items():
subgraph = g.subgraph(nodes)
pos_subgraph = nx.spring_layout(subgraph, **kwargs)
pos.update(pos_subgraph)
return pos
def test():
# to install networkx 2.0 compatible version of python-louvain use:
# pip install -U git+https://github.com/taynaud/python-louvain.git#networkx2
from community import community_louvain
g = nx.karate_club_graph()
partition = community_louvain.best_partition(g)
pos = community_layout(g, partition)
nx.draw(g, pos, node_color=list(partition.values())); plt.show()
return
Addendum
Although the general idea is sound, my old implementation above has a few issues. Most importantly, the implementation doesn't work very well for unevenly sized communities. Specifically, _position_communities gives each community the same amount of real estate on the canvas. If some of the communities are much larger than others, these communities end up being compressed into the same amount of space as the small communities. Obviously, this does not reflect the structure of the graph very well.
I have written a library for visualizing networks, which is called netgraph. It includes an improved version of the community layout routine outlined above, which also considers the sizes of the communities when arranging them. It is fully compatible with networkx and igraph Graph objects, so it should be easy and fast to make great looking graphs (at least that is the idea).
import matplotlib.pyplot as plt
import networkx as nx
# installation easiest via pip:
# pip install netgraph
from netgraph import Graph
# create a modular graph
partition_sizes = [10, 20, 30, 40]
g = nx.random_partition_graph(partition_sizes, 0.5, 0.1)
# since we created the graph, we know the best partition:
node_to_community = dict()
node = 0
for community_id, size in enumerate(partition_sizes):
for _ in range(size):
node_to_community[node] = community_id
node += 1
# # alternatively, we can infer the best partition using Louvain:
# from community import community_louvain
# node_to_community = community_louvain.best_partition(g)
community_to_color = {
0 : 'tab:blue',
1 : 'tab:orange',
2 : 'tab:green',
3 : 'tab:red',
}
node_color = {node: community_to_color[community_id] for node, community_id in node_to_community.items()}
Graph(g,
node_color=node_color, node_edge_width=0, edge_alpha=0.1,
node_layout='community', node_layout_kwargs=dict(node_to_community=node_to_community),
edge_layout='bundled', edge_layout_kwargs=dict(k=2000),
)
plt.show()
I am trying to build a hierarchical directed network where some nodes can branch into others, while others do not. The values in the inner dictionary (i.e. the integers) are to keep track of leaves in the tree-like structure. I've created a naive way to turn this particular nested dictionary graph_data into a directed graph but it is only specific to 3 layers. Below shows the hierarchy:
How can I create a nested function that adds edges to the directed graph for any number of levels? For example, if there was a level-3 or a level-4 this would not work and I would have to expand it out each time. Do I need to use a while loop?
import numpy as np
from collections import *
import networkx as nx
%matplotlib inline
# Hierarchical data
graph_data = {"root": {"level-0.A":0,
"level-0.B":{"level-1.B.1":2,
"level-1.B.2": {"level-2.B.2.1":3, "level-2.B.2.2":1}}}}
# Empty directed graph
G = nx.DiGraph()
# Helper functions
is_dict = lambda x: type(x) in {dict, OrderedDict, defaultdict}
# Iterate through the layers
for root, level_0 in graph_data.items():
if len(level_0) > 0:
for level_0_node, level_1 in level_0.items():
G.add_edge(root, level_0_node)
if is_dict(level_1):
for level_1_node, level_2 in level_1.items():
G.add_edge(level_0_node, level_1_node)
if is_dict(level_2):
for level_2_node, level_3 in level_2.items():
G.add_edge(level_1_node, level_2_node)
np.random.seed(8)
nx.draw(G, with_labels=True)
Use a queue to hold the details, e.g.:
from collections import Mapping
graph_data = {"root": {"level-0.A":0,
"level-0.B":{"level-1.B.1":2,
"level-1.B.2": {"level-2.B.2.1":3, "level-2.B.2.2":1}}}}
# Empty directed graph
G = nx.DiGraph()
# Iterate through the layers
q = list(graph_data.items())
while q:
v, d = q.pop()
for nv, nd in d.items():
G.add_edge(v, nv)
if isinstance(nd, Mapping):
q.append((nv, nd))
np.random.seed(8)
nx.draw(G, with_labels=True)
This question already has answers here:
Bipartite graph in NetworkX
(4 answers)
Closed 7 years ago.
I have an n1-by-n2 bi-adjacency matrix A of a bipartite graph. The matrix A is a scipy.sparse csc matrix. I would like to plot the bipartite graph using A in networkx. Assume that the nodes are colored according to their class labels called node_class. I could do the following:
import networkx as nx
G = nx.from_numpy_matrix(A)
graph_pos = nx.fruchterman_reingold_layout(G)
degree = nx.degree(G)
nx.draw(G, node_color = node_class, with_labels = False, node_size = [v * 35 for v in degree.values()])
The above code works fine for a square dense adjacency matrix. However not for a non-square bi-adjacency matrix A. The error is:
'Adjacency matrix is not square.'
Moreover the matrix A I have is a scipy.sparse matrix` because it is very large and have lots of zeros. So I would want to avoid making an (n1+n2)-by-(n1+n2) adjacency matrix by stacking A and adding zeros.
I checked the documentation of NetworkX for bipartite graphs, it does not mention how to plot bi-partite graph using bi-adjacency matrix, or create a graph using bi-adjacency sparse matrix. If someone could tell me how to plot the bipartite graph, that would be great!
I don't believe there is a NetworkX function that creates a graph from a biadjacency matrix, so you'll have to write your own. (However, they do have a bipartite module you should check out.)
Here's one way to define a function that takes a sparse biadjacency matrix and converts it to a NetworkX graph (see the comments for explanation).
# Input: M scipy.sparse.csc_matrix
# Output: NetworkX Graph
def nx_graph_from_biadjacency_matrix(M):
# Give names to the nodes in the two node sets
U = [ "u{}".format(i) for i in range(M.shape[0]) ]
V = [ "v{}".format(i) for i in range(M.shape[1]) ]
# Create the graph and add each set of nodes
G = nx.Graph()
G.add_nodes_from(U, bipartite=0)
G.add_nodes_from(V, bipartite=1)
# Find the non-zero indices in the biadjacency matrix to connect
# those nodes
G.add_edges_from([ (U[i], V[j]) for i, j in zip(*M.nonzero()) ])
return G
See an example use case below, where I use nx.complete_bipartite_graph to generate a complete graph:
import networkx as nx, numpy as np
from networkx.algorithms import bipartite
from scipy.sparse import csc_matrix
import matplotlib.pyplot as plt
RB = nx.complete_bipartite_graph(3, 2)
A = csc_matrix(bipartite.biadjacency_matrix(RB, row_order=bipartite.sets(RB)[0]))
G = nx_graph_from_biadjacency_matrix(A)
nx.draw_circular(G, node_color = "red", with_labels = True)
plt.show()
And here's the output graph:
Here is a simple example:
import networkx as nx
import matplotlib.pyplot as plt
from networkx.algorithms import matching
%matplotlib inline
ls=[
[0,0,0,1,1],
[1,0,0,0,0],
[1,0,1,0,0],
[0,1,1,0,0],
[1,0,0,0,0]
]
g = nx.Graph()
a=['a'+str(i) for i in range(len(ls))]
b=['b'+str(j) for j in range(len(ls[0]))]
g.add_nodes_from(a,bipartite=0)
g.add_nodes_from(b,bipartite=1)
for i in range(len(ls)):
for j in range(len(ls[i])):
if ls[i][j] != 0:
g.add_edge(a[i], b[j])
pos_a={}
x=0.100
const=0.100
y=1.0
for i in range(len(a)):
pos_a[a[i]]=[x,y-i*const]
xb=0.500
pos_b={}
for i in range(len(b)):
pos_b[b[i]]=[xb,y-i*const]
nx.draw_networkx_nodes(g,pos_a,nodelist=a,node_color='r',node_size=300,alpha=0.8)
nx.draw_networkx_nodes(g,pos_b,nodelist=b,node_color='b',node_size=300,alpha=0.8)
# edges
pos={}
pos.update(pos_a)
pos.update(pos_b)
#nx.draw_networkx_edges(g,pos,edgelist=nx.edges(g),width=1,alpha=0.8,edge_color='g')
nx.draw_networkx_labels(g,pos,font_size=10,font_family='sans-serif')
m=matching.maximal_matching(g)
nx.draw_networkx_edges(g,pos,edgelist=m,width=1,alpha=0.8,edge_color='k')
plt.show()
I create a spring layout network of the shortest path from a given node. In this case firm1. I want to have a different color for each degree of separation. For instance, all the first edge connecting firm1 and the other firms, say firm2 and firm3, I would like to change the node color of firm2 and firm3 (same color for both). Then all the firms connected from firm2 and firm3, say firm4 and firm5 I want to change their node colors. But I don't know how to change the colors of the node for each degree of separation starting from firm1. Here's my code:
import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd
graph = nx.Graph()
with open('C:\\file.txt') as f: #Here, I load a text file with two columns indicating the connections between each firm
for line in f:
tic_1, tic_2 = line.split()
graph.add_edge(tic_1, tic_2)
paths_from_1 = nx.shortest_path(graph, "firm1") #I get the shortest path starting from firm1
x = pd.DataFrame(paths_from_1.values()) #I convert the dictionary of the shortest path into a dataframe
tic_0=x[0].tolist() #there are 7 columns in my dataframe x and I convert each columns into a list. tic_0 is a list of `firm1` string
tic_1=x[1].tolist() #tic_1 is list of all the firms directly connected to firm1
tic_2=x[2].tolist() #tic_2 are the firms indirectly connected to firm1 via the firms in tic_1
tic_3=x[3].tolist() #and so on...
tic_4=x[4].tolist()
tic_5=x[5].tolist()
tic_6=x[6].tolist()
l = len(tic_0)
graph = nx.Graph()
for i in range(len(tic_0)):
graph.add_edge(tic_0[i], tic_1[i])
graph.add_edge(tic_1[i], tic_2[i])
graph.add_edge(tic_2[i], tic_3[i])
graph.add_edge(tic_3[i], tic_4[i])
graph.add_edge(tic_4[i], tic_5[i])
graph.add_edge(tic_5[i], tic_6[i])
pos = nx.spring_layout(graph_short, iterations=200, k=)
nx.draw(graph_short, pos, font_size='6',)
plt.savefig("network.png")
plt.show()
How can I have different color nodes for each degree of separation? In other words, all the firms in tic_1 should have a node that is blue, all the firms in tic_2 has a yellow node color, etc.
The generic way to do this is to run the shortest path length algorithm from a source node to assign the colors. Here is an example:
import matplotlib.pyplot as plt
import networkx as nx
G = nx.balanced_tree(2,5)
length = nx.shortest_path_length(G, source=0)
nodelist,hops = zip(*length.items())
positions = nx.graphviz_layout(G, prog='twopi', root=0)
nx.draw(G, positions, nodelist = nodelist, node_color=hops, cmap=plt.cm.Blues)
plt.axis('equal')
plt.show()
You could use
positions = nx.spring_layout(G)
instead. I used the graphviz circo layout since it does a better job at drawing the balanced tree I used.