Is there a way to plot a graph using two different layouts, one for a set of nodes and another for all other nodes?
E.g., define that nodes 1-10 are plotted with circular layout and all other nodes are drawn with force-directed layout.
Yes you can. You just need to hack together two different layouts.
library(igraph)
gr <- random.graph.game(100, p.or.m = 0.25, type = "gnp")
lay1 <- layout_in_circle(induced_subgraph(gr, 1:20)) ##layouts are just matrices with x, y coordinates
lay2 <- layout_with_fr(induced_subgraph(gr, 21:100)) #I used Fruchterman-Reingold on the subgraph excluding the nodes in the circle but you could include them and then overwrite their layout coordinates with the coordinates for the circle
lay3 <- rbind(lay1+2, lay2) ## I added a scalar to shift the circlular nodes out of the middle of the force-directed layout to make it more obvious.
plot(gr, layout=lay3, vertex.size=8)
Related
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 the following dataframe:
df<-data.frame(consumed= c("level1_plt1", "level1_plt2", "level1_plt3", "level1_plt3","level1_plt2","level1_plt4","level1_plt5","level1_plt5","level1_plt6","level1_plt7","level1_plt8","level1_plt9","level1_plt10","level1_plt10","level1_plt1","level1_plt1","level1_plt6","level1_plt6","level1_plt9","level1_plt9","level1_plt11","level1_plt11","level1_plt11","level2_lep1","level2_lep4","level2_lep3"),consumer=c("level2_lep1","level2_lep2","level2_lep3","level2_lep2","level2_lep4", "level2_lep4","level2_lep5","level2_lep5","level2_lep6","level2_lep7","level2_lep8","level2_lep9","level2_lep10","level2_lep10","level2_lep8","level2_lep8","level2_lep1","level2_lep1","level2_lep3","level2_lep11","level2_lep12","level2_lep13","level2_lep13", "level3_pst1","level3_pst3","level3_pst4"))
And have preformed the following steps to get an igraph tripartite output:
links<-
df%>%
group_by(consumed, consumer) %>%
summarize(freq=n())
g<- graph_from_data_frame(d=links,directed=FALSE)
layer <- rep(2, length(V(g)$name))
layer[grepl("level1_",V(g)$name)]=1
layer[grepl("level3_",V(g)$name)]=3
names<- V(g)$name
names<-sub("level2_","", names)
names<-sub("level3_","", names)
names<-sub("level1_","", names)
V(g)$name = names
layout = layout_with_sugiyama(g, layers=layer)
E(g)$width <- E(g)$freq
V(g)$vertex_degree <- degree(g)*7
plot(g,
layout=cbind(layer,layout$layout[,1]),edge.curved=0,
vertex.shape=c("square","circle","square")[layer],
vertex.frame.color = c("darkolivegreen","darkgoldenrod","orange3")
[layer],
vertex.color=c("olivedrab","goldenrod1","orange1")[layer],
vertex.label.color="white",
vertex.label.font=2,
vertex.size=V(g)$vertex_degree,
vertex.label.dist=c(0,0,0)[layer],
vertex.label.degree=0, vertex.label.cex=0.5)
And I would like to do two things to adjust the picture, if possible:
Order the layers from the largest shape (highest degree) to smallest shape (smallest degree). For example, in the green layer the order could be as follows: plt9, plt3,plt2,plt11,plt6,plt1,plt7,plt5,plt4,plt10,plt8.
Create space between the shapes so that there is no overlap (e.g. lep3 and lep4). I like the current sizes/proportions so I am opposed to making shapes smaller to create space between shapes.
Flip the graph and vertex font 90 degrees counter-clockwise so that from bottom to top it would be in the order green layer-->yellow layer-->orange layer. (I guess it is always an option to rotate vertex text and I can rotate the image in word or ppt.)
I know this question is old, but I hope that the answer will help someone.
Rather than using layout_with_sugiyama, It may be easiest to do this with
a custom layout. It is not very hard to do so. You already constructed the
horizontal position with your layer variable. To get the vertical positions,
we need to order the vertices by size (vertex_degree) and then allow shape proportional to the size, so we will set the height using cumsum on the vertex_degrees within each layer. After I make the layout the complex call to plot is the same as yours except
that I swap my custom layout for your call to sugiyama.
MyLO = matrix(0, nrow=vcount(g), ncol=2)
## Horizontal position is determined by layer
MyLO[,1] = layer
## Vertical position is determined by sum of sorted vertex_degree
for(i in 1:3) {
L = which(layer ==i)
OL = order(V(g)$vertex_degree[L], decreasing=TRUE)
MyLO[L[OL],2] = cumsum(V(g)$vertex_degree[L][OL])
}
plot(g,
layout=MyLO, edge.curved=0,
vertex.shape=c("square","circle","square")[layer],
vertex.frame.color = c("darkolivegreen","darkgoldenrod","orange3")[layer],
vertex.color=c("olivedrab","goldenrod1","orange1")[layer],
vertex.label.color="white",
vertex.label.font=2,
vertex.size=V(g)$vertex_degree,
vertex.label.dist=0,
vertex.label.degree=0, vertex.label.cex=0.5)
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.
I'm trying to create a special graph layout where 2 different types of nodes (based on their attribute) are placed on 2 different circles with different radius (concentric circles layout).
Here's a toy example where a graph with 10 nodes have an attribute (size). The goal is to place the nodes with size less than 5 on an inner circle, and the nodes with size greater than 5 on an outer circle:
g <- make_full_graph(10)
V(g)$size = V(g)
I couldn't find any such layout supported by igraph library. Does anyone know how to achieve this?
There is the layout_in_circle option if you only wanted one circle. You could apply that separately to each of your groups with something like this
layout_in_circles <- function(g, group=1) {
layout <- lapply(split(V(g), group), function(x) {
layout_in_circle(induced_subgraph(g,x))
})
layout <- Map(`*`, layout, seq_along(layout))
x <- matrix(0, nrow=vcount(g), ncol=2)
split(x, group) <- layout
x
}
Then you could plot with
plot(g, layout=layout_in_circles(g, group=V(g)>5))
It doesn't do anything special to try to make the edges pretty. But I guess the point is you can define whatever function you want to control the layout by returning a matrix of coordinates.
Let g be an igraph object. For example, g <- make_graph(~A-C-B, C-D, E-D-F). And let us set up a vertex attribute called level
V(g)[c("A", "B")]$level <- 1
V(g)[c("C")]$level <- 2
V(g)[c("D")]$level <- 3
V(g)[c("E", "F")]$level <- 4
Are there any tools in igraph to build a layout for g such that it respects level in a meaning that a vertex with less level is always placed to the left and vertices with same level have the same (or close) abscissa.
So, for the given graph I'd like to see a picture like this:
Since a layout in igraph is just a matrix of {x,y} coordinates, you can set the x-coordinates equal to your levels.
g <- make_graph(~A-C-B, C-D, E-D-F)
V(g)$level <- c(1,2,1,3,4,4)
l <- matrix(c(V(g)$level,1,2,3,2,3,1),nrow=length(V(g)$level),ncol=2)
plot(g, layout=l)
I just did the y-axis by hand, but you can construct it as you see fit.
Using Sugiyama layout
Sugiyama layout works by adding layers. There are a lot of options with the layout, but, basically, it tries to create a hierarchical representation of the graph.
l <- layout_with_sugiyama(g, layers = -V(g)$level)$layout
#note the "-", this ensures that the smaller level values get small x coordinates
plot(g,layout=l[,c(2,1)])