How to retain the general shape of a network in igraph? - r

I currently have a network graph in igraph with which I am running simulations to see how the frequency of traits change over time like so:
g <- erdos.renyi.game(1000, 1/1000)
V(g)$nice <- sample(c(0, 1), vcount(g), replace = TRUE, prob = c(0.1, 0.9)
Following this I have a working code that modifies the the network across several "turns". The problem arises when I graph the network. I initially graph the network at t = 0 and once more at t = 20 or so to compare the two and see how they have changed. However, the location of the nodes have changed from the initial to the final. Is there a way that I can retain the location of the nodes in the actual graph? (i.e. so that node 4 will remain at some coordinate (a, b) despite changes in the network)

You can repeat the same layout by using the layout argument to plot. First, you create a layout using one of the many layout_ arguments, then just call plot specifying the layout. If you plot again with the same layout, the nodes will be in the same place.
LO_FR = layout_with_fr(g)
plot(g, layout=LO_FR, vertex.size=4, vertex.label=NA,
main="layout_with_fr")
LO_N = layout_nicely(g)
plot(g, layout=LO_N, vertex.size=4, vertex.label=NA,
main="layout_nicely")
Type help(package=igraph) and then scroll down to the functions whose names start with layout_. Try several and pick one that you like.

Related

cluster walktrap returns three communities, but when plotting they are all on top of each other, with no visible clustering

I've been following documentation tutorials and even lecture tutorials step by step. But for some reason the output of my plot is like this:
The output doesn't make any sense to me. There clearly is no structure, or communities in this current plot, as you can see that the bigger circles are all overlapping. Shouldn't this, in this case, return only a single community? Additionally the modularity of my network is ~0.02 which would again, suggest there is no community structure. But why does it return 3 communities?
this is my code: (exactly same as in documentation, with different dataset)
m <- data.matrix(df)
g <- graph_from_adjacency_matrix(m, mode = "undirected")
#el <- get.edgelist(g)
wc <- cluster_walktrap(g)
modularity(wc)
membership(wc)
plot(wc,g)
my data set looks is a 500x500 adjacency matrix in the form of a csv, with a 1-500 column and index names corresponding to a person.
I tried understanding the community class and using different types of variables for the plot, e.g. membership(wc)[2] etc. My thought is that the coloring is simply wrong, but nothing Ive tried so far seems to fix the issue.
You can have inter-community connections. You're working with a graph of 500 nodes and they can have multiple connections. There will be a large number of connections between nodes of different communities, but if you conduct a random walk you're most likely to traverse connections between nodes of the same community.
If you separate the communities in the plot (using #G5W's code (igraph) Grouped layout based on attribute) you can see the different groups.
set.seed(4321)
g <- sample_gnp(500, .25)
plot(g, vertex.label = '', vertex.size = 5)
wc <- cluster_walktrap(g)
V(g)$community <- membership(wc)
E(g)$weight = 1
g_grouped = g
for(i in unique(V(g)$community)){
groupV = which(V(g)$community == i)
g_grouped = add_edges(g_grouped, combn(groupV, 2), attr=list(weight = 2))
}
l <- layout_nicely(g_grouped)
plot( wc,g, layout = l, vertex.label = '', vertex.size = 5, edge.width = .1)
Red edges are intercommunity connections and black edges are intracommunity edges

How to spread or unclutter a network?

I am trying to spread out or better visualize the network. The issue is the number of isolates I have seems to spread out the isolates and force the clusters into compact spaces that makes it hard to see patterns.
Does anyone have any guides to spread the clusters or better visualize the data. If you need, I can edit in the code.
Thanks
Here are two ways to get a better layout. Both solutions require just a little experimentation to find good settings. First, since you do not provide any data, I will make an example that is vaguely like yours.
library(igraph)
set.seed(1234)
g1 = erdos.renyi.game(100,0.35)
g2= erdos.renyi.game(10,0.35)
G = add_vertices(g1+g2, 50)
LO = layout_with_fr(G)
plot(G, layout=LO, vertex.size=5, vertex.label="")
Solution #1 Adjust edge weights
If you don't need to use the edge weights for anything else, you can just set the weights to something low so that there will not be such a strong attraction between connected points. You may need to experiment to find a good value to use for the weights.
E(G)$weight = 0.01
LO = layout_with_fr(G)
plot(G, layout=LO, vertex.size=5, vertex.label="")
Solution #2 Directly adjust the layout
You could also directly adjust the layout.
First, I identify which are the nodes that are too tightly clustered.
CM = components(G)$membership
table(CM)
For my graph, component 1 is the one that I want to expand. Next, I just shift the layout so that it is centered on the centroid of the first component. This does not change the graph; it just makes it easy for me to adjust the layout.
LO = layout_with_fr(G)
LO[,1] = LO[,1] - mean(LO[CM == 1,1])
LO[,2] = LO[,2] - mean(LO[CM == 1,2])
But now I can grow (or shrink) the first component by any given expansion factor. I choose one (5.5) to take up most of the empty space.
LO[components(G)$membership == 1,] = LO[components(G)$membership == 1,] * 5.5
plot(G, layout=LO, vertex.size=5, vertex.label="")
Of course if you wanted to go back and adjust other components, you could do that too.

Looking to save coordinates/layout to make temporal networks in Igraph with DRL

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)

Automatically curving an arc when it is overlapping with another one

I am automatically generating graphs whose nodes need to be in fixed positions. For example:
There is actually an arc from node V4 to node V16, but we annot see it because there are also arcs from V4 to V10 and from V10 to V16.
Note that both the nodes and the arcs are generated automatically, and that the positions may vary, so I would need an automated way to curve arcs that are hidden behind other arcs.
Also note that none of these solutions are valid: igraph: Resolving tight overlapping nodes ; Using igraph, how to force curvature when arrows point in opposite directions. The first one simply places de nodes in a certain way, but my nodes need to be fixed. The second one simply deals with pairs of nodes that have two arcs connecting them going in the opposite direction.
UPDATE: The construction of the graph is the result of the learning process of the graph that forms a Bayesian Network using bnlearn library, so I am not very sure how could I produce a reproducible example. The positions of the nodes are fixed because they represent positions. I actually need some magic, some kind of detection of overlapping arcs: If two arcs overlap, curve one of them slightly so that it can be seen. I know from the linked questions that curving an arc is an option, so I thought maybe this kind of magic could be achieved
One solution would be to use the qgraph package. In the example below it automatically curves the bidirectional edges:
library(igraph)
library(qgraph)
# the raster layout
layout <- cbind(1:3, rep(1:3, each = 3))
# fully connected network
adj <- matrix(1, 9, 9)
# plot directed and undirected network
layout(matrix(1:2, 1, 2))
qgraph(adj, layout = layout, directed = FALSE, title = "undirected")
qgraph(adj, layout = layout, directed = TRUE, title = "directed") # automatically curves the bidirectional arrows
To convert an igraph object to something qgraph can use, all you need is an edgelist or adjacency matrix:
g <- make_ring(9)
edgeList <- do.call(rbind, igraph::get.adjedgelist(g))
qgraph(edgeList)
If you also want to include the axes, you can do so using axis() since qgraph uses base graphics. However, you probably have to tinker with par() as well to make it look nice.

Draw Network in R (control edge thickness plus non-overlapping edges)

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

Resources