This question is about attempting to model interdependent networks with NetworkX. There are dedicated packages (such as Pymnet), but they don't seem as flexible as NetworkX. And by the way, I wanted to give NetworkX one last chance.
So, let's say we have 2 separate graphs, G1 and G2, which we plot in the same figure:
import networkx as nx
import matplotlib.pyplot as plt
G1=nx.barabasi_albert_graph(3, 2) #n=3, m=2 (number of initial links)
G2=nx.barabasi_albert_graph(3, 2)
pos1=nx.spring_layout(G1)
pos2=nx.spring_layout(G2)
nx.draw_networkx(G1,pos=pos1,node_color='red') #G1 is red
nx.draw_networkx(G2,pos=pos2,node_color='green') #G2 is green
Now, if we attempt to connect node 0 of G1 with node 1 of G2:
G1.add_edge(G1.nodes()[0], G2.nodes()[1])
we don't get any error, but if you plot the graphs again, the image is exactly as before. And if you check the number of edges you get the same results as before:
In[17]: G1.edges()
Out[17]: [(0, 1), (0, 2), (1, 2)]
In[18]: G2.edges()
Out[18]: [(0, 2), (1, 2)]
meaning that the edge was basically not added, or it was added and is not displayed, or it was added, but because it runs from one graph to another, it doesn't belong to any of them.
How do you suggest to create this interconnection from G1 to G2 in NetworkX, without resorting to other packages?
I think the fundamental issue is that you've got a different concept for how networkx thinks of a graph from what it is. I believe you're thinking that the nodes of the graph are objects of some node class that the nodes themselves intrinsically have some attribute saying what their position is. This is not the case. There is no special node class. A graph can have any hashable object as its nodes because really, a graph is just a fancy dict whose keys are what we call nodes.
The nodes of your graph G1 are the integers 0, 1, and 2. G2 has exactly the same nodes. The new edge you've added is between whatever integer is in G1.nodes()[0] and whatever integer is in G2.nodes()[1]. In your example I believe G1 already has that edge.
Separately, you've created two different dicts pos1 and pos2 (which have the same keys - the integer values that form the nodes of the two graphs). These dicts say where the nodes should be plotted. You've told it to plot G1 using pos1. So it puts the circle for the node that is 0 at pos1[0] and similarly for 1 and 2. Then when you later tell it to plot G using pos1 it's going to do exactly the same thing.
What you probably want to do is create a new graph whose nodes consist of the nodes of G1 and of G2, after renaming so that they aren't the same nodes. This is done by union (see documentation.)
G = union(G1, G2, rename=('G1-', 'G2-'))
Then add the edge, noting that the nodes in G have different names, so the command you used won't quite work.
Related
I am working with networkx 2.5. in Jupyter notebook, python version 3.6.5 I am working with an indirect graph. I have a graph from which I would like to perform the following steps iteratively
Remove one edge,
Perform some calculations over the remaining graph (number of connected components, diameter and nodes)
Put back the removed edge
Remove another edge
Repeat the whole procedure until all the edges were deleted once
For instance, I have the following graph:
'target' : [3, 2 ,4]})
nodes2 = pd.DataFrame({'nodeS' : [1, 2, 3 , 4],
'density' : [1, 2, 4, 2],
'indiv' : [2, 4, 1, 5]})
G1 = nx.from_pandas_edgelist(edges2, 'source', 'target')
nx.set_node_attributes(G1, pd.Series(nodes2.density, index=nodes2.nodeS).to_dict(), "density")
nx.set_node_attributes(G1, pd.Series(nodes2.indiv, index=nodes2.nodeS).to_dict(), 'indiv')
I plotted the first graph (G1)
The result looks like
Then I make the calculations over the graph:
# largest connected component (it will be necessary for the first calculation after first edge removal)
G1_LCC = G1.subgraph(max(nx.connected_components(G), key=len))
# number of connected components
G1_cc = nx.number_connected_components(G1)
# diameter of largest connected component
G1_diam = nx.diameter(G1_LCC)
# number of connected nodes
G1_nodes = len(nx.nodes(G1_LCC))
Removing an edge:
I tried the next:
e = (1,2)
G2= nx.remove_edge(e)
But it gives me an error:
nx.edges_iter(G)
AttributeError: module 'networkx' has no attribute 'remove_edge'
This should look like (G2)
Then perform operations on G2
Remove an edge which would look like that:
The third G3, (G3)
and so on, until all the edges have been removed once.
Note: I tried with edges iter
nx.edges_iter(G)
but I also got an error
AttributeError: module 'networkx' has no attribute 'edges_iter'
Is there an iterative/ efficient way to remove the edges, make calculations and put them back? Rather than do it one by one? Thank you
remove_edge is a method of the Graph class, not a top-level function in the library. So G2 = G1.remove_edge(e).copy() would be the correct command (you may not need to make a copy if you are adding the edge back in at a later point). edges_iter was also a method of Graph but has been discontinued for edges. So for e in G1.edges(), not for e in nx.edges_iter(G1).
how can I create a complete list of dyads from a vertex list?
I have a list (1, 2, 3...) and I need to generate a list containing all possible dyads from that list (1-1, 1-2, 1-3, 2-1, 2-2,...).
I've tried with get.edgelist, but it doesn't work, because the graph is not fully connected (all nodes are connected among them).
Thanks
Using igraph, you can grab all edges of a graph using E(g). If you'd want all possible edges, you can apply it on a complete graph (a graph that is fully connected). If the vertices in your graph are indeed in sequence from 1 to n, you can use make_full_graph() to make a Kn - that is to say a fully connected graph. In this example, the graph has 14 vertices.
g <- make_full_graph(14, directed=F)
el <- as_edgelist(g)
edges <- E(g)
edges_list <- split(el, rep(1:nrow(el), each = ncol(el)))
edges_vert <- unlist(list(t(el)))
edges will be the igraph-object, but I think what you're after is a list in R, like edges_list.
As you see, length(edges_list) is 91 since it is an undirected graph, and the number of edges in complete graphs is a function of the number of vertices.
A complete graph with n vertices is commonly written Kn and has these many edges:
Note that in igraph dyads are called edges and nodes are called vertices.
I would like to create temporal networks in R but the only resources I've found works with FR or KK graphs. However, my primary graph that I would like to base the layout from is a DRL layout. How could I code this in R to keep the layouts?
Thank you
Added:
Code:
drl <- layout.drl(netfull, options=list(simmer.attraction=0))
plot(netfull, edge.arrow.size=2, vertex.size=.5, vertex.label.cex=.3, vertex.label.dist=.1, vertex.lable.degree=pi, layout=drl)
plot(net7, edge.arrow.size=2, vertex.size=.5,vertex.label.cex=.3, vertex.label.dist=.1, vertex.lable.degree=pi, layout=drl)
You can just explicitly compute your layout before plotting and then use the layout argument when you want to plot. DRL is one of the standard options provided by igraph.
library(igraph)
## create test graph
set.seed(1234)
g = erdos.renyi.game(15, 0.2, type = "gnp")
## Create a reusable layout for the graph
LO = layout_with_drl(g)
## plot using the layout
plot(g, layout=LO)
Edit
Based on the discussion in the comments, I have a different understanding of the question. I think that the question is this: Given a graph g and a subgraph g2 print both g and g2 with the corresponding nodes in the same place. This extra response addresses that.
Start with the example above to create the graph g and the layout LO.
Now we want to take a subgraph and print it with the corresponding nodes in the same place. I will use as an example the graph that we get by removing nodes 2, 9, and 15.
If we simply remove those nodes, the new graph will have 12 nodes and they will have node IDs 1-12. In order to preserve the original numbering, we need to save the node IDs as labels.
V(g)$label = 1:15
Now let's create the subgraph by removing nodes 2,9 and 15.
g2 = induced_subgraph(g, V(g)[-c(2,9,15)])
We want to reuse the layout LO, but LO has the positions for all 15 original nodes. We want to select only the part for the remaining nodes in g2.
LO2 = LO[-c(2,9,15),]
Now we are ready to plot the original graph and the reduced graph so that the nodes line up.
par(mfrow=c(1,2), mar=c(2,1,2,1))
plot(g, layout=LO, frame=TRUE)
plot(g2, layout=LO2, frame=TRUE)
I have 4 undirected graph with 1000 vertices and 176672, 150994, 193477, 236060 edges. I am trying to see interaction between a specific set of nodes (16 in number) for each graph. This visualization in tkplot is not feasible as 1000 vertices is already way too much for it. I was thinking of if there is some way to extract the interaction of these 16 nodes from the parent graph and view separately, which will be then more easy to handle and work with in tkplot. I don't want the loss of information as in what is the node(s) in he path of interaction if it comes from other than 16 pre-specified nodes. Is there a way to achieve it?
In such a dense graph, if you only take the shortest paths connecting each pair of these 16 vertices, you will still get a graph too large for tkplot, or even to see any meaningful on a cairo pdf plot.
However, if you aim to do it, this is one possible way:
require(igraph)
g <- erdos.renyi.game(n = 1000, p = 0.1)
set <- sample(1:vcount(g), 16)
in.shortest.paths <- NULL
for(v in set){
in.shortest.paths <- c(in.shortest.paths,
unlist(get.all.shortest.paths(g, from = v, to = set)$res))
}
subgraph <- induced.subgraph(g, unique(in.shortest.paths))
In this example, subgraph will include approx. half of all the vertices.
After this, I think you should consider to find some other way than visualization to investigate the relationships between your vertices of interest. It can be some topological metric, but it really depends on the aims of your analysis.
I need to draw a network with 5 nodes and 20 directed edges (an edge connecting each 2 nodes) using R, but I need two features to exist:
To be able to control the thickness of each edge.
The edges not to be overlapping (i.e.,the edge form A to B is not drawn over the edge from B to A)
I've spent hours looking for a solution, and tried many packages, but there's always a problem.
Can anybody suggest a solution please and provide a complete example as possible?
Many Thanks in advance.
If it is ok for the lines to be curved then I know two ways. First I create an edgelist:
Edges <- data.frame(
from = rep(1:5,each=5),
to = rep(1:5,times=5),
thickness = abs(rnorm(25)))
Edges <- subset(Edges,from!=to)
This contains the node of origin at the first column, node of destination at the second and weight at the third. You can use my pacake qgraph to plot a weighted graph using this. By default the edges are curved if there are multiple edges between two nodes:
library("qgraph")
qgraph(Edges,esize=5,gray=TRUE)
However this package is not really intended for this purpose and you can't change the edge colors (yet, working on it:) ). You can only make all edges black with a small trick:
qgraph(Edges,esize=5,gray=TRUE,minimum=0,cut=.Machine$double.xmin)
For more control you can use the igraph package. First we make the graph:
library("igraph")
g <- graph.edgelist(as.matrix(Edges[,-3]))
Note the conversion to matrix and subtracting one because the first node is 0. Next we define the layout:
l <- layout.fruchterman.reingold(g)
Now we can change some of the edge parameters with the E()function:
# Define edge widths:
E(g)$width <- Edges$thickness * 5
# Define arrow widths:
E(g)$arrow.width <- Edges$thickness * 5
# Make edges curved:
E(g)$curved <- 0.2
And finally plot the graph:
plot(g,layout=l)
While not an R answer specifically, I would recommend using Cytoscape to generate the network.
You can automate it using a RCytoscape.
http://bioconductor.org/packages/release/bioc/html/RCytoscape.html
The package informatively named 'network' can draw directed networks fairly well, and handle your issues.
ex.net <- rbind(c(0, 1, 1, 1), c(1, 0, 0, 1), c(0, 0, 0, 1), c(1, 0, 1, 0))
plot(network(ex.net), usecurve = T, edge.curve = 0.00001,
edge.lwd = c(4, rep(1, 7)))
The edge.curve argument, if set very low and combined with usecurve=T, separates the edges, although there might be a more direct way of doing this, and edge.lwd can take a vector as its argument for different sizes.
It's not always the prettiest result, I admit. But it's fairly easy to get decent looking network plots that can be customized in a number of different ways (see ?network.plot).
The 'non overlapping' constraint on edges is the big problem here. First, your network has to be 'planar' otherwise it's impossible in 2-dimensions (you cant connect three houses to gas, electric, phone company buildings without crossovers).
I think an algorithm for planar graph layout essentially solves the 4-colour problem. Have fun with that. Heuristics exist, search for planar graph layout, and force-directed, and read Planar Graph Layouts