Draw graph with time label on the nodes - python

I have a (directed acyclic) graph, such that each node have a discrete time-stamp i.e., each node is labeled with a number : 1,2,3,4...
The graph might have more than one component, and the time-stamp is increasing with the direction of the graph.
I have the graph as netowrkx object, and now I want to create a figure in python with the graph s.t. a node's height is determined by it's time-stamp (lower time-stamps at the top and higher at the bottom and nodes with the same time-stamps are at the same height). Meaning that for instance, 2 components may "begin" (their root's height) at different initial heights if their time-stamps are different.
on the other hand, given node A labeled with time = 4, and node B, labeled 5, I want B to be lower in the figure.
3rd example: if 2 nodes are children of a third node but they were created in different times - the edge will be longer for the latest one, s.t. each children will be in its own time-level.
write now, the code I have is very simple:
import networkx as nt
import matplotlib.pyplot as plt
graph = networkx.DiGraph()
self.graph.add_node("A", time = 1)
...# Add more nodes
graph.add_edge("A","B")
...# Add more edges
plt.figure(1,figsize=(30,10))
pos=nt.graphviz_layout(graph,prog='dot')
networkx.draw(graph,pos,with_labels=False,arrows=True, node_size = 80)
(I don't have enough reputation to upload image of the results... :-) )
How can I draw this in Python? Which package can help me here?

The positions of the nodes are defined in a dictionary, with key the name of the node and value a tuple with the x and y coords.
You can see this by printing the pos variable once created:
pos=nt.graphviz_layout(graph,prog='dot')
print pos
# EXAMPLE RESULT
{'A': (51.5, 15.3), 'B': (20.0, 60.4)}
A higher value in the y coordinate will place the node higher in the graph. So if you want the root nodes to be at the top (lower time stamp value), just use the negative of the time stamps as -1 will be higher than -2.
To automate the process of placing the nodes, you need some equation based on time.
pos = {}
for key, value in graph.node.items():
height = value['time'] # Time value as defined when adding the node `key`
pos[key] = (50, -height)
This will place the nodes vertically, based on their time stamps. So the distance between two nodes created at times 1 and 4 and will be twice as large than two nodes created at times 1 and 2. In this example I kept the horizontal component static (50) and all nodes will end up in a vertical line. You should be able to vary that according to your needs.

Related

Looking to connect the nodes that are at a certain maximum distance R in Networkx

I want to take the number of nodes "N" from the user and plot them randomly in a 2D surface MxM.
Next is to connect nodes say i,j ∈ N if and only if their distance R is <= to any constant threshold.
G = nx.DiGraph()
n_size=input("Please enter number of nodes: ")
n_size = int(n_size)
for i in range(n_size):
G.add_node(i)
fig1, ax1 = plt.subplots(figsize=(6, 6))
nx.draw(G, with_labels=True, font_weight='normal')
plt.axis('on')
plt.show()
A couple of options to consider. For both of them, your beginning block of code is the same:
G = nx.DiGraph()
n_size=input("Please enter number of nodes: ")
n_size = int(n_size)
for i in range(n_size):
G.add_node(i)
fig1, ax1 = plt.subplots(figsize=(6, 6))
The positions of the nodes need to be determined in advance of creating edges, however, so you have to use one of the NetworkX layouts to do that. Since you mentioned wanting a random node layout, that's what I'll be using here, but there are many other options. I'll also add a threshold value for the distance of 0.3 that will be used later.
pos = nx.random_layout(G)
threshold = 0.3
This creates a position dictionary with node names as keys and coordinates as values. As far as the actual options for creating edges based on a distance threshold, you could do one of the following.
Using built-in nx.geometric_edges
NetworkX has a convenient geometric_edges function that takes a threshold distance value and returns a list of edges for nodes that fall within that distance from one another.
The geometric_edges function requires that each node have a pos attribute associated with it, so we can add that as follows:
for node_name in G.nodes:
G.node[node_name]['pos'] = pos[node_name]
This just takes the value from the position dictionary and adds it to each node of the graph as a pos attribute.
Next, we can simply use the geometric_edges method, which returns a list of edges that are less than a specified distance from one another. There is also a p argument required for the method that defaults to a value of 2 if no other value is specified. 2 indicates Euclidean distance, which I'm assuming is what you want, so I'll leave the default as is.
edges_to_add_list = nx.geometric_edges(G, threshold, p = 2)
G.add_edges_from(edges_to_add_list)
This method only creates an edge in one direction in a directed graph, so you'd need to add reversed edges if you need them to be bi-directional.
Manually calculating distance
If you'd like to have a bit more flexibility to only select nodes that are above (rather than below) a certain distance from one another, you can calculate the distance and add edges yourself. This will add edges in both directions between nodes and can show self-loops as well if you want it to. This method assumes you have numpy installed as np.
for node_a in G.nodes:
for node_b in G.nodes:
if np.sqrt((pos[node_a][0]-pos[node_b][0])**2 + (pos[node_a][1]-pos[node_b][1])**2) <= threshold and node_a != node_b:
G.add_edge(node_a, node_b)
If you want self-loops to show up, just eliminate the node_a != node_b part.
Here's just an example of the second method with 15 nodes:

Finding and fixing label islands in networkx

I have a graph where each node has an integer label. If the graph is well-behaved, labeled regions will be continuous. I'd like to write something in python networkx to "fix" bad graphs. For example, in
I'd like to:
1) identify bad nodes (the ones in dotted blue lines); then
2) remove their label and "fill" with the correct value
My graph vocabulary is weak; are there networkx functions that can do this?
Note: not sure if it makes a difference, but and all nodes have a degree of 3, and the graph is always a topological sphere.
1) For each label, make a subgraph of your original graph containing all nodes with that label (networkx.subgraph).
2) For each subgraph, find the connected components with networkx.connected_components, which returns a generator of node sets, one for each component.
3) For each component that is not the largest component of its class, find the neighbours of each node with networkx.neighbors; determine their labels and assign the most common label (that is not also the label of the component) to the nodes in that component.
This procedure may fail if large mislabeled "islands" are adjacent to each other but should work for the example shown.

How to locate the center node of a giant component in NetworkX?

I am working with a regular network which has the shape of a 2D grid. It is made of NxN=100x100=10000 nodes. It has a coordinate system where node 0 is in the top left corner, the (0,0) position, and node 10000 is in the bottom right corner, the (100,100) position.
The network is created like this:
N=100 #The number of nodes per side
G=nx.grid_2d_graph(N,N)
pos = dict( (n, n) for n in G.nodes() ) #Dictionary of all positions
labels = dict( ((i, j), i + (N-1-j) * N ) for i, j in G.nodes() )
nx.set_node_attributes(G, 'pos', pos) #Store pos attributes in the nodes
nx.set_node_attributes(G, 'labels', labels) #Store labels attributes in the nodes
nx.draw_networkx(G, pos=nx.get_node_attributes(G, 'pos'),
labels=nx.get_node_attributes(G, 'labels'),
with_labels=False, node_size=10)
This network gets fragmented as a result of its response to a number of loadings. These are a number of csv files which values are used as input for the nodes. After the failure, this is what the network looks like (this is the result of a single loading file):
My question: how can I determine the location of the center node of the giant component, and determine its distance from the top left corner, for instance, which has coordinates (0,0)?
EDIT
Due to the variability in response, very rarely there will be a node at position (0,0), so using nx.shortest_path() would be pointless in that the said node will be most of the times missing. Therefore, I want to measure the distance between one point of the network (the center of the giant component), and another point of the same "region", which may not be part of the network. So, the function nx.shortest_path() cannot be used, or it would throw an error when the path does not exist.
First retrieve the giant component of you graph with: (referenced here)
giant = max(nx.connected_component_subgraphs(G), key=len)
Retrieve the center nodes with:
center_nodes = center(giant)
The location of the node is the center node itself because the keys are the locations. So to display the location of center nodes for instance:
print center_nodes
To determine the distance from node one of the center nodes to (i,j) coordinates, you have to keep a copy of the original graph with all the 100x100 nodes. I will use it here as org_G
# i,j can represent any coordinate in the 100x100 grid (0,0) for instance
n = (i,j)
print nx.shortest_path(org_G,n,center_nodes[0])

Creating fixed set of nodes using networkx in python

I have a problem concerning graph diagrams. I have 30 nodes(points). I want to construct an adjacency matrix in such a way that each ten set of nodes are like at a vertices of a triangle. So lets say a group of 10 nodes is at the vertex A, B and C of a triangle ABC.
Two of the vertex sets should have only 10 edges(basically each node within a cluster is connected to other one). Lets say groups at A and B have 10 edges within the group. While the third vertex set should have 11 edges(10 for each nodes and one node connecting with two nodes, so 11 edges in that group). Lets say the one at C has 11 edges in it.
All these three clusters would be having one edge between them to form a triangle.That is connect group at A with group at B with one edge and B with C with one edge and C with A with one edge.
Later on I would add one more edge between B and C. Represented as dotted line in the attached figure. The point at a vertex can be in a circle or any other formation as long as they represent a group.
How do I create an adjacency matrix for such a thing. I actually know how to create the adjacency matrix for such a matrix as it is just binary symmetric matrix(undirected graph) but the problem is when I try to plot that adjacency matrix it would bring the one node from other group closer to the group to which that node is connected. So lets say I connected one node at Vertex A with one node at Vertex B by connecting an edge between the two. This edge would depict the side AB of the triangle. But when I depict it using networkx then those two nodes which are connected from these two different groups would eventually come closer and look like part of one group. How do I keep it as separate group. ?
Pls note I am making use of networkx lib of python which helps plot the adjacency matrix.
EDIT:
A code I am trying to use after below inspiration:
G=nx.Graph()
# Creating three separate groups of nodes (10 nodes each)
node_clusters = [range(1,11), range(11,21) , range(21,31)]
# Adding edges between each set of nodes in each group.
for x in node_clusters:
for y in x:
if(y!=x[-1]):
G.add_edge(y,y+1,len=2)
else:
G.add_edge(y,x[0],len=2)
# Adding three inter group edges separately:
for x in range(len(node_clusters)):
if(x<2):
G.add_edge(node_clusters[x][-1],node_clusters[x+1][0],len=8)
else:
G.add_edge(node_clusters[x][-1],node_clusters[0][0],len=8)
nx.draw_graphviz(G, prog='neato')
Gives the following error:
--> 260 '(not available for Python3)')
261 if root is not None:
262 args+="-Groot=%s"%root
ImportError: ('requires pygraphviz ', 'http://networkx.lanl.gov/pygraphviz ', '(not available for Python3)')
My python version is not 3, its 2. And am using anaconda distribution
EDIT2:
I used Marius's code but instead used the following to plot:
graph_pos=nx.spring_layout(G,k=0.20,iterations=50)
nx.draw_networkx(G,graph_pos)
It has destroyed completely the whole graph. and shows this:
I was able to get something going fairly quickly just by hacking away at this, all you need to do is put together tuples representing each edge, you can also set some arbitrary lengths on the edges to get a decent approximation of your desired layout:
import networkx
import string
all_nodes = string.ascii_letters[:30]
a_nodes = all_nodes[:10]
b_nodes = all_nodes[10:20]
c_nodes = all_nodes[20:]
all_edges = []
for node_set in [a_nodes, b_nodes, c_nodes]:
# Link each node to the next
for i, node in enumerate(node_set[:-1]):
all_edges.append((node, node_set[i + 1], 2))
# Finish off the circle
all_edges.append((node_set[0], node_set[-1], 2))
joins = [(a_nodes[0], b_nodes[0], 8), (b_nodes[-1], c_nodes[0], 8), (c_nodes[-1], a_nodes[-1], 8)]
all_edges += joins
# One extra edge for C:
all_edges.append((c_nodes[0], c_nodes[5], 5))
G = networkx.Graph()
for edge in all_edges:
G.add_edge(edge[0], edge[1], len=edge[2])
networkx.draw_graphviz(G, prog='neato')
Try something like networkx.to_numpy_matrix(G) if you then want to export as an adjacency matrix.

NetworkX graph: creating nodes with ordered list

I am completely new to graphs. I have a 213 X 213 distance matrix. I have been trying to visualize the distance matrix using network and my idea is that far apart nodes will appear as separate clusters when the graph will be plotted. So I am creating a graph with nodes representing column index. I need to keep track of nodes in order to label it afterwards. I need to add edges in certain order so I need to keep track of nodes and their labels.
Here is the code:
import networkx as nx
G = nx.Graph()
G.add_nodes_from(time_pres) ##time_pres is the list of labels that I want specific node to have
for i in range(212):
for j in range(i+1, 212):
color = ['green' if j == i+1 else 'red'][0]
edges.append((i,j, dist[i,j], 'green')) ##This thing requires allocation of distance as per the order in dist matrirx
G.add_edge(i,j, dist = dist[i,j], color = 'green')
The way I am doing right now, it is allocating nodes with id as a number which is not as per the index of labels in time_pres.
I can answer the question you seem to be asking, but this won't be the end of your troubles. Specifically, I'll show you where you go wrong.
So, we assume that the variable time_pres is defined as follows
time_pres = [('person1', '1878'), ('person2', '1879'), etc)]
Then,
G.add_nodes_from(time_pres)
Creates the nodes with labels ('person1', '1878'), ('person2', '1879'), etc. These nodes are held in a dictionary, with keys the label of the nodes and values any additional attributes related to each node. In your case, you have no attributes. You can also see this from the manual online, or if you type help(G.add_nodes_from).
You can even see the label of the nodes by typing either of the following lines.
G.nodes() # either this
G.node.keys() # or this
This will print a list of the labels, but since they come from a dictionary, they may not be in the same order as time_pres. You can refer to the nodes by their labels. They don't have any additional id numbers, or anything else.
Now, for adding an edge. The manual says that any of the two nodes will be added if they are not already in the graph. So, when you do
G.add_edge(i, j, dist = dist[i,j], color = 'green')
where, i and j are numbers, they are added in the graph since they don't already exist in the graph labels. So, you end up adding the nodes i and j and the edge between them. Instead, you want to do
G.add_edge(time_pres[i], time_pres[j], dist = dist[i,j], color = 'green')
This will add an edge between the nodes time_pres[i] and time_pres[j]. As far as I understand, this is your aim.
However, you seem to expect that when you draw the graph, the distance between nodes time_pres[i] and time_pres[j] will be decided by the attribute dist=dist[i,j] in G.add_edge(). In fact, the position of a node is decided by tuple holding the x and y positions of the node. From the manual for nx.draw().
pos : dictionary, optional
A dictionary with nodes as keys and positions as values. If not specified a spring layout positioning will be computed. See networkx.layout for functions that compute node positions.
If you don't define the node positions, they will be generated randomly. In your case, you would need a dictionary like
pos = {('person1', '1878'): (23, 10),
('person2', '1879'): (18, 11),
etc}
Then, the coordinates between the nodes i and j would result to a distance equal to dist[i,j]. You would have to figure out these coordinates, but since you haven't made it clear exactly how you derived the matrix dist, I can't say anything about it.

Categories