I am going to draw a network with python3 and networkxmoduel.
First of all, I am sorry I could not write any example code because I did not receive any raw data.
The network consists of 3 groups of nodes and a below attached is what I imagined.
It is a hand drawn.
I would like to refer any layout or tips to draw this kind of above.
I know Multipartite Layouthttps://networkx.org/documentation/stable/auto_examples/drawing/plot_multipartite_graph.html#multipartite-layout, however, I am not sure it is suitable for me.
Thanks.
The multi-partite layout is going to put your nodes in rows/columns based on the partitions you specify, but it seems like what you want is to align your nodes so that the provided groups/partitions are clustered together and separated from the other groups/clusters. You can do this by making a position dictionary that you can pass to the networkx drawing functions. The example function below takes your graph, the name of the node attribute in your Graph object that specifies which group/partition each node belongs to (partition_attr), an optional list of partition names specifying the order you want to display your groups/components left-to-right (partition_order) and the minimum space between nodes in different partitions (epsilon).
#%% Function to make position dicts by partition
def make_node_positions(graph,partition_attr,partition_order=None,epsilon=.5):
if not partition_order:
# get a list of all the partition names if not specified
partition_order = list(set(dict(graph.nodes(data=partition_attr)).values()))
# make position dict for each partition
orig_partition_pos_dicts = {partition:nx.spring_layout(graph.subgraph([node for node,part in graph.nodes(data=partition_attr)
if part == partition]))
for partition in partition_order}
# update the x coordinate in the position dicts so partitions
# don't overlap and are in the specified order left-to-right
final_pos_dict = orig_partition_pos_dicts[partition_order[0]]
for i,partition in enumerate(partition_order[1:]):
# get the largest x coordinate from the previous partition's nodes
max_previous = max([x for x,y in final_pos_dict.values()])
# get smallest x coordinate from this partition's nodes
current_min = min([x for x,y in orig_partition_pos_dicts[partition].values()])
# update the x coordinates for this partition to be at least epsilon units
# to the right of the right-most node in the previous partition
final_pos_dict.update({node:(pos[0]+max_previous+abs(current_min)+epsilon,pos[1])
for node,pos in orig_partition_pos_dicts[partition].items()})
return(final_pos_dict)
Now I've made a graph similar to your drawing and applied the function below
#%% Set up toy graph
import networkx as nx
# make the initial graphs
k5 = nx.complete_graph(5)
triangle=nx.from_edgelist([(5,6),(6,7),(5,7)])
single_node = nx.Graph()
single_node.add_node(8)
# edges to connect the components
extra_edges = [(3,5),(2,6),(5,8),(6,8),(7,8)]
# combine graphs and specify the original graphs
orig_graphs = {'k5':{'graph':k5,'color':'blue'},
'triangle':{'graph':triangle,'color':'green'},
'single_node':{'graph':single_node,'color':'red'}}
g = nx.Graph()
for g_name,g_val_dict in orig_graphs.items():
# add the nodes from that graph and specify the partition and node colors
g.add_nodes_from(g_val_dict['graph'].nodes,partition=g_name,color=g_val_dict['color'])
if len(g_val_dict['graph'].edges) > 0:
# if the graph has edges then add the edges
g.add_edges_from(g_val_dict['graph'].edges,partition=g_name,color=g_val_dict['color'])
# add the extra edges to combine the graphs
g.add_edges_from(extra_edges,color='black')
#%% Draw graph #####
my_pos = make_node_positions(g,partition_attr='partition',partition_order=['k5','triangle','single_node'])
nx.draw_networkx_nodes(g,my_pos,node_color=[c for n,c in g.nodes(data='color')])
nx.draw_networkx_labels(g,my_pos)
nx.draw_networkx_edges(g,my_pos,edge_color=[c for u,v,c in g.edges(data='color')])
Related
This is related to this question, with a small difference. Namely, I am already given a graph G, which is a bi-partite graph, meaning that there exist two sets of vertices, set U and set I, and the connection could only exist between a node from the set U and a node from the set I.
I want to extend this unconnected graph by making it connected and still bi-partite, and I also want that the probability of a new edge (when extending the graph with edges) is proportional to the nodes degrees (i.e., higher probability that an edge links two nodes of large degrees). The code that I would like to extend:
import random
from itertools import combinations, groupby
components = dict(enumerate(nx.connected_components(G)))
components_combs = combinations(components.keys(), r=2)
for _, node_edges in groupby(components_combs, key=lambda x: x[0]):
node_edges = list(node_edges)
random_comps = random.choice(node_edges)
source = random.choice(list(components[random_comps[0]]))
target = random.choice(list(components[random_comps[1]]))
G.add_edge(source, target)
I am working with networks undergoing a number of disrupting events. So, a number of nodes fail because of a given event. Therefore there is a transition between the image to the left to that to the right:
My question: how can I find the disconnected subgraphs, even if they contain only 1 node? My purpose is to count them and render as failed, as in my study this is what applies to them. By semi-isolated nodes, I mean groups of isolated nodes, but connected to each other.
I know I can find isolated nodes like this:
def find_isolated_nodes(graph):
""" returns a list of isolated nodes. """
isolated = []
for node in graph:
if not graph[node]:
isolated += node
return isolated
but how would you amend these lines to make them find groups of isolated nodes as well, like those highlighted in the right hand side picture?
MY THEORETICAL ATTEMPT
It looks like this problem is addressed by the Flood Fill algorithm, which is explained here. However, I wonder how it could be possible to simply count the number of nodes in the giant component(s) and then subtract it from the number of nodes that appear still active at stage 2. How would you implement this?
If I understand correctly, you are looking for "isolated" nodes, meaning the nodes not in the largest component of the graph. As you mentioned, one method to identify the "isolated" nodes is to find all the nodes NOT in the largest component. To do so, you can just use networkx.connected_components, to get a list of the components and sort them by size:
components = list(nx.connected_components(G)) # list because it returns a generator
components.sort(key=len, reverse=True)
Then you can find the largest component, and get a count of the "isolated" nodes:
largest = components.pop(0)
num_isolated = G.order() - len(largest)
I put this all together in an example where I draw a Erdos-Renyi random graph, coloring isolated nodes blue:
# Load modules and create a random graph
import networkx as nx, matplotlib.pyplot as plt
G = nx.gnp_random_graph(10, 0.15)
# Identify the largest component and the "isolated" nodes
components = list(nx.connected_components(G)) # list because it returns a generator
components.sort(key=len, reverse=True)
largest = components.pop(0)
isolated = set( g for cc in components for g in cc )
# Draw the graph
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos=pos, nodelist=largest, node_color='r')
nx.draw_networkx_nodes(G, pos=pos, nodelist=isolated, node_color='b')
nx.draw_networkx_edges(G, pos=pos)
plt.show()
I have a directed tree graph with a single root vertice, where the directions seem arbitrary.
I want all edges to be directed from the single root, towards the ends of the respective branches.
My first primitive attempt was to swap the source and target vertices like below (but as assumed, it would not work.
temp = g.es[e_idx].source
g.es[e_idx].source = g.es[e_idx].target
g.es[e_idx].target = temp
Is there a function or a set of functions that allow to swap the direction of a specific edge available?
Or a way to manipulate the source / target attributes of edges, without needing to change any vertice attributes?
Please let me know if I need to specify anything further.
Your help would be greatly appreciated.
Here is a possible solution that preserves all graph attributes:
We are going to reorder vertices so that those closer to the root come first
Then we use to_directed with the "acyclic" mode, which directs edges from lower to higher index vertices
Finally we restore the original vertex order
import igraph as ig
from igraph import Graph
# Set seed for reproducibility
import random
random.seed(123)
# Create an undirected tree. If your tree is not undirected,
# convert it to undirected first.
g = Graph.Tree_Game(10)
# Our chosen root:
root = 3
ig.plot(g, vertex_label=range(g.vcount()), layout = g.layout_reingold_tilford(root=root), bbox=(300,300))
# Distances from the root, will be used for ordering:
dist=g.shortest_paths(source=root)[0]
# This function computes the permutation that would
# sort 'elems'. It also serves as a way to invert
# permutations.
def ordering(elems):
return sorted(range(len(elems)), key=elems.__getitem__)
# Compute orderings based on the distance from the root:
perm = ordering(dist)
invperm = ordering(perm)
# Reorder, direct, restore order:
dg = g.permute_vertices(invperm)
dg.to_directed('acyclic')
dg = dg.permute_vertices(perm)
# Plot again.
# Now the root does not need to be given,
# as it is auto-detected from the directions.
ig.plot(dg, vertex_label=range(g.vcount()), layout='reingold_tilford', bbox=(300,300))
I need to build something like an Erdos-Renyi model (random graph):
I need to create it from a dictionary of node positions that is generated by a deterministic function. This means that I cannot allow Python to randomly decide where each node goes to, as I want to decide it. The function is:
pos = dict( (n, n) for n in G.nodes() ).
I was thinking of creating an adjacency matrix first, in order to randomly generate something similar to pairs of (start, endpoint) of each edge, like this:
G=np.random.randint(0, 1, 25).reshape(5, 5)
Then I was thinking of somehow turning the matrix into my list of edges, like this:
G1=nx.grid_2d_graph(G)
but of course it does not work since this function takes 2 args and I am only giving it 1.
My questions:
How to create this kind of graph in NetworkX?
How to make sure that all nodes are connected?
How to make sure that, upon assigning the 1 in the matrix, each pair of nodes has the same probability of landing a 1?
Example for point 3. Imagine we created the regular grid of points which positions are determined according to pos. When we start connecting the network and we select the first node, we want to make sure that the endpoint of this first edge is one of the N-1 nodes left in the network (except the starting node itself). Anyhow, we want to make sure that all N-1 nodes have the same probability of being connected to the node we first analyze.
Thanks a lot!
I will try to build on the previous questions concerning this problem to be consistent. Given that you have the keys of the grid_2d_graph as 'n' and not (i,j) with the relabel nodes function:
N = 10
G=nx.grid_2d_graph(N,N)
pos = dict( (n, n) for n in G.nodes() )
labels = dict( ((i, j), i + (N-1-j) * N ) for i, j in G.nodes() )
nx.relabel_nodes(G,labels,False)
now you can set the pos dictionary to map from the 'n' keyed nodes to the positions you already have by switching keys with values. And then simply call the Erdos-Renyi function to create the graph that has a probability 'p' that an edge exists between any two nodes (except a self edge) as you described in point 3. Then draw with the pos dictionary.
pos = {y:x for x,y in labels.iteritems()}
G2 = nx.erdos_renyi_graph(100,0.1)
nx.draw_networkx(G2, pos=pos, with_labels=True, node_size = 300)
print G.nodes()
plt.axis('off')
plt.show()
As for ensuring that the graph is connected in point 2 You can not guarantee that the graph is connected with a probability 1, but you can read a bit about the size of the Giant component in Erdos-Renyi graph. But to avoid getting into theoretical details, one is almost sure that the graph will be connected when lambda which is n*p (here they are 100*0.1) is greater than 4. Though for smaller graphs (like 100 nodes) you better increase lambda. From my own experience using n= 100 and p = 0.1, produced a non-connected graph for only about 0.2% of the times and that is after thousands of simulations. And anyway you can always make sure if the produced graph is connected or not with the is_connected method.
I have a graph where I have previously precomputed the positions of the nodes by running ForceAtlas2 in Gephi and then I attempt to draw with ìgraph in python. Each node has a date attribute so I iterate over all years, compute the corresponding subgraphs and draw them to create an animation effect.
However as nodes keep being added to the graph, the previous nodes don't necessarily keep their old positions, similarly to what is described in this post
My code is as follows:
colors = [color_list[int(g.vs[v]["Modularity Class"])] for v in range(0, len(g.vs))]
label_sizes = [sz / 4.0 for sz in g.vs["size"]]
nodes_in_years = defaultdict(list) # keeps a list of nodes per year
for v in g.vs:
year = int(v["date"][:4]) # convert the first 4 digits of the date to year
nodes_in_years[year].append(v)
nodes_incremental = []
for y, nodes in nodes_in_years.items():
nodes_incremental += nodes
h = g.subgraph(nodes_incremental)
plot(h, "graph-igraph%d.png"%y,
bbox=(640,480),
vertex_color=colors,
vertex_label_size=label_sizes,
keep_aspect_ratio=False,
)
The following are two consecutive snapshots.
In the second snapshot nodes are slightly "squeezed" as more nodes are added to the left.
How can I keep the nodes at fixed positions? I tried with xlim setting and ylim but I might not be setting the values right
xlim and ylim are for the R interface of igraph; they have no effect in Python. The usual trick to employ is to find the minimum and maximum X and Y coordinates across all snapshots, and then place two fake vertices (with their shape attribute set to None) in the upper left and lower right corners of the layout. Make sure that you include these fake vertices in all snapshots with the same coordinates - this will ensure that the layouts are scaled exactly the same way in each snapshot to fit the bounding box that you specify.
If you did not use the shape attribute with your current layout, note that adding a shape attribute to the fake vertices will also add the shape attribute (with the default value of None) to the "real" vertices, so you should manually set the shape attribute of all vertices except the two fake ones to "circle".