Plotting directed multigraphs in R - r

I've never used any graph plotting package in R, I'm familiar with basic plotting commands and with ggplot2 package. What I've found (but not tried out yet) are Rgraphviz, network and igraph packages. So I'd like to ask you, which package has simplest learning curve and satisfies following requirements:
Has simple layout engines (spring layout, random, ...)
Tries to draw multiple edges between two vertices so that they would not overlap. As a bonus it would be nice to being able to adjust this.
Can draw loops.
Vertex and edge labels, vertex and edge size and color are adjustable.
(No need for any of the graph algorithms like link analysis, shortest path, max flow etc, but nice, if present)

The igraph package seems to fulfill your requirements, with the tkplot() function helping adjusting the final layout if needed.
Here is an example of use:
s <- cbind(A=sample(letters[1:4], 100, replace=TRUE),
B=sample(letters[1:2], 100, replace=TRUE))
s.tab <- table(s[,1], s[,2])
library(igraph)
s.g <- graph.incidence(s.tab, weighted=T)
plot(s.g, layout=layout.circle,
vertex.label=c(letters[1:4],letters[2:1]),
vertex.color=c(rep("red",4),rep("blue",2)),
edge.width=c(s.tab)/3, vertex.size=20,
vertex.label.cex=3, vertex.label.color="white")
With the interactive display (there's a possibility of using rgl for 3D display), it looks like (I have slightly moved one vertex afterwards):
tkplot(s.g, layout=layout.circle, vertex.color=c(rep("red",4),rep("blue",2)))
Finally, you can even export you graph into most common format, like dot for graphviz.

The multigraph R package can be useful as well. For the above example bmgraph plots such graph:
library("multigraph")
bmgraph(s.tab, layout = "circ", pch = 16:16, pos = 0, vcol = 6:7, lwd = 3, cex = 9)
And for a directed version:
bmgraph(s.tab, "circ", pch = 16:16, pos = 0, vcol = 6:7, lwd = 3, cex = 9, directed = TRUE)

Related

Defining Node shape in a Network plot with an additional attribute table in R

I am working on plotting a Network and it contains two different types of Nodes which I want to visualise with different shapes. For that I made an additional table in which I specified which structure is which type using a binary system. Now I want to specify in my plot function that the structures with 1 are to be triangles and the ones with 0 as circles.
My data for the Network is in the format of an adjacency matrix (I use igraph) and I am using ggnet2 for the plotting of it.
this is how I imported the data:
am <- as.matrix(read.csv2("mydata.csv", header = T, row.names = 1))
g <- graph_from_adjacency_matrix(am, mode = "undirected")
attr <- read.csv2("myattributes.csv", header = T, row.names = 1)
this is how I would plot it but I dont know how to specify the shape function
ggnet2(g, size = "degree", node.color = "darkgreen", shape = ??????)
Thanks in advance for your help!
Note that the package-requirements for plotting igraphs with ggnet2 include ggplot2, sna and network as well as intergraph as a bridge.
ggnet2 is prettier, sure, but the igraph-way is this:
g <- erdos.renyi.game(100,100,'gnm')
V(g)$shape <- sample(c('csquare','circle'), 100, replace=T)
plot(g, vertex.label = NA)
Note that I added two igraph-style shapes as vertex-attributes to g above. In ggent2 you can provide a vector with shapes, but they can be any values (even a factor), or numbers (the usual gray circle is 19. Try this out to plot in ggnet2
ggnet2(g, shape=19)
ggnet2(g, shape=10+round(1:100/10))
ggnet2(g, shape=factor(V(g)$shape))
V(g)$shape <- sample(c('One shape','Another shape'), 100, replace=T)
ggnet2(g, shape=V(g)$shape, size = "degree", node.color = "darkgreen")
Note that, if you add attributes to your vertices after separately loading attribute data (as you do above), it may be so that the very order of your data matters. Make sure your table import actually works as intended with the correct attribute being assigned to the correct vertex. I find it a good practice to tie all values as attributes on the igraph-object (edge- and vertex attributes alike) rather than letting the network data live in different dataframes or loose vectors to be combined in order to correctly visualise a network.

R: Plot too large to fit into window

I made a phylogenetic tree plot with the igraph package.
The problem is, that my tree is too big to fit into the saved image.
I'm using Rstudio and usually saving my plots manually.
With smaller trees, I was able to enlarge the plot window of Rstudio to make the plot fit into the image, without the nodes overlapping each other.
Unfortunately, this isn't working with my latest plot.
I tried to save it with png(), pdf() and jpeg() with different width and height, but it still doesn't fit properly.
Either the nodes overlap each other or only a part of the plot is visible in the image.
The only solution I found so far was to decrease the label size. But in the end, you can't read anything.
Thank you in advance for your help.
Here is my code:
id <- c("Spirochaetota","Brachyspirae","Brevinematia","Leptospirae","Spirochaetia","sk6","Brachyspirales","Brachyspiraceae","Brachyspira",
"Brevinematales","bo1","Brevinemataceae","Brevinema","bf2","Leptospirales","Leptonemataceae","Leptonema","lg2","Leptospiraceae","Leptospira",
"LeptospiraA","LeptospiraB","lg3","lf1","Turneriellales","Turneriellaceae","Turneriella","tf1","Borreliales","Borreliaceae","Borrelia",
"Borreliella","Sphaerochaetales","Sphaerrochaetaceae","Sphaerochaeta","SphaerochaetaA","sg15","SpirochaetalesA","SpirochaetaceaeA",
"SpirochaetaA","sf1","SpirochaetalesC","Alkalispirochaetaceae","Alkalispirochaeta","Salinispiraceae","Salinispira","SpirochaetaD","sg1",
"sf6","SpirochaetalesD","sdf1","SpirochaetalesE","SpirochaetaceaeB","Oceanispirochaeta","SpirochaetaE","SpirochaetaF","SpirochaetaG","sg2",
"Treponematales","Treponemataceae","Treponema","TreponemaB","TreponemaC","TreponemaD","TreponemaF","tg7","TreponemataceaeB","TreponemaE",
"TreponemaG","TreponemaH","tg5","tf3","so11")
links <- data.frame(from = c("Spirochaetota","Spirochaetota","Spirochaetota","Spirochaetota","Spirochaetota","Brachyspirae",
"Brachyspirales","Brachyspiraceae","Brevinematia","Brevinematia","Brevinematales","Brevinemataceae",
"Brevinematales","Leptospirae","Leptospirales","Leptonemataceae","Leptonemataceae","Leptospirales",
"Leptospiraceae","Leptospiraceae","Leptospiraceae","Leptospiraceae","Leptospirales","Leptospirae",
"Turneriellales","Turneriellaceae","Turneriellales","Spirochaetia","Borreliales","Borreliaceae",
"Borreliaceae","Spirochaetia","Sphaerochaetales","Sphaerrochaetaceae","Sphaerrochaetaceae","Sphaerrochaetaceae",
"Spirochaetia","SpirochaetalesA","SpirochaetaceaeA","SpirochaetalesA","Spirochaetia","SpirochaetalesC",
"Alkalispirochaetaceae","SpirochaetalesC","Salinispiraceae","Salinispiraceae","Salinispiraceae","SpirochaetalesC",
"Spirochaetia","SpirochaetalesD","Spirochaetia","SpirochaetalesE","SpirochaetaceaeB","SpirochaetaceaeB",
"SpirochaetaceaeB","SpirochaetaceaeB","SpirochaetaceaeB","Spirochaetia","Treponematales","Treponemataceae",
"Treponemataceae","Treponemataceae","Treponemataceae","Treponemataceae","Treponemataceae","Treponematales",
"TreponemataceaeB","TreponemataceaeB","TreponemataceaeB","TreponemataceaeB","Treponematales","Spirochaetia"),
to = c("Brachyspirae","Brevinematia","Leptospirae","Spirochaetia","sk6","Brachyspirales",
"Brachyspiraceae","Brachyspira","Brevinematales","bo1","Brevinemataceae","Brevinema",
"bf2","Leptospirales","Leptonemataceae","Leptonema","lg2","Leptospiraceae",
"Leptospira","LeptospiraA","LeptospiraB","lg3","lf1","Turneriellales",
"Turneriellaceae","Turneriella","tf1","Borreliales","Borreliaceae","Borrelia",
"Borreliella","Sphaerochaetales","Sphaerrochaetaceae","Sphaerochaeta","SphaerochaetaA","sg15",
"SpirochaetalesA","SpirochaetaceaeA","SpirochaetaA","sf1","SpirochaetalesC","Alkalispirochaetaceae",
"Alkalispirochaeta","Salinispiraceae","Salinispira","SpirochaetaD","sg1","sf6",
"SpirochaetalesD","sdf1","SpirochaetalesE","SpirochaetaceaeB","Oceanispirochaeta","SpirochaetaE",
"SpirochaetaF","SpirochaetaG","sg2","Treponematales","Treponemataceae","Treponema",
"TreponemaB","TreponemaC","TreponemaD","TreponemaF","tg7","TreponemataceaeB",
"TreponemaE","TreponemaG","TreponemaH","tg5","tf3","so11"))
net <- graph_from_data_frame(d = links, vertices = id, directed = T)
lay = layout.reingold.tilford(net)
plot(net, vertex.shape = "none",
vertex.label.font = 3,
vertex.label.cex = 0.3,
edge.arrow.size = 0.3,
rescale = F,
ylim = c(0.7,3.4),xlim = c(-8,23.2), asp = 0,
layout = lay)
If everything works, it should look similar to this smaller tree. this one does not include the nodes of the code above:
This is my result of the big tree so far. It contains the nodes in the code above:
The only solution I came up with so far is to somehow fit the plot into the plot panel and save it as a SVG file. Afterwards, I edited it using inkscape:
You can plot the tree graph as a dendrogram.
Drawing it left to right allows to put all of the taxon labels in a readable column:
library(igraph)
net <- graph_from_data_frame(d = links, vertices = id, directed = F)
lay <- layout.reingold.tilford(net)
plot_dendrogram(cluster_fast_greedy(net))
There is also:
links %>%
graph_from_data_frame() %>%
as_adjacency_matrix() %>%
dist() %>%
hclust() %>%
as.dendrogram() %>%
plot()

Adjust edge label position in mediation diagram with DiagrammeR?

I am trying to draw a standard triangular mediation diagram using DiagrammeR in R (it can also interpret graphviz code). On the whole, it's working fine but the edge label text gets placed oddly. The bottom edge label is not centered and the two angled edge labels are positioned at different heights (see the red lines in the diagram below). Is there a way to manually assign positions to edge text or get something more consistent?`
library(DiagrammeR)
# Create a node data frame (ndf)
ndf <- create_node_df(
n = 3,
label = c("Experimental\nTreatment", "Some\nMediator", "Outcome\nof Interest"),
shape = rep("rectangle", 3),
style = "empty",
fontsize = 6,
fixedsize = TRUE,
height = .5,
width = .75,
color = "gray40",
x = c(1, 2, 3),
y = c(1, 2, 1)
)
# Create an edge data frame (edf)
edf <- create_edge_df(
from = c(1, 1, 2),
to = c(2, 3, 3),
label = c("1.1*", "2.0*", "-0.33***"),
fontsize = 6,
minlen = 1,
color = "gray40",
)
# Create a graph with the ndf and edf
graph <- create_graph(
nodes_df = ndf,
edges_df = edf
)
graph %>%
render_graph()
I would also love to know the answer to this.
I have tried to delete the default "layout" graph attribute and set direction of graph layout via:
graphAttr <- DiagrammeR::get_global_graph_attrs(graph)
But, I get the following error:
Error: 'get_global_graph_attrs' is not an exported object from 'namespace:DiagrammeR'
....which it is, consequently, I am stuck.
After fiddling with DiagrammeR for a while, I ended up switching to the LaTeX diagramming package TikZ. It allows for enormous control of every aspect of the diagram but can be overwhelming. TikZ would require a bit of tweaking, also, to get diagrams into HTML output if that's important. For a presentation using LaTeX's Beamer class for slides, generating TikZ code with a function in R worked well. That TikZ code was then written to a text file that could be imported automatically via the LaTeX command \input{path/to/file.txt} or just copied and pasted into my slides.
See the code for two DiagrammeR solutions and the TikZ solution, here: https://stackoverflow.com/a/64886536/893399

R: Drawing markov model with diagram package (making diagram changes)

I have the following code that draws a transition probability graph using the package heemod (for the matrix) and the package diagram (for drawing). The following code generates such a graph with data that I have generated:
library('heemod')
library('diagram')
mat_dim <- define_transition(
state_names = c('State_A', 'State_B', 'State_C'),
.18, .73, .09,
.12, .10, .78,
.58, .08, .33);
plot(mat_dim)
This creates the following plot:
My questions all originate from my poor understanding of the diagram package and I can't figure out these seemingly simple adaptations...
How can I move the arrow at State_B around (e.g.90 degrees to one side) so that it does not overlap with other arrows? Is there a simple way to move the states closer together?
See ?plotmat.
argument curve, a matrix, to control the curvatures of the "non-self" transitions
arguments self.shiftx and self.shifty to control the positions of the self-transitions
argument self.arrpos to control the positions of the self-arrows
This is really not easy. Here is what I obtained by a lot of trial-errors.
curves <- matrix(nrow = 3, ncol = 3, 0.05)
plot(mat_dim,
curve=curves,
self.shiftx = c(0.1,-0.1,0),
self.shifty = c(-0.1,-0.1,0.15),
self.arrpos = c(1,2.1,1))

R igraph vertex spacing

I'm using igraph to plot networks and can't seem to get nodes (vertices) not drawn over top of each other.
My code:
g<-graph.empty(n=0, directed=FALSE)
nodes<-my_verts
edge<-my_intra_edges
freq<-nodes[,2]
max_freq<-sum(freq)
frequency<-freq*50/max_freq
colour1<-heat.colors(max_freq/2+2)
colour<-rev(colour1)
g<-igraph::add.vertices(g, length(nodes[,2]), name=as.character(nodes[,1]), color=colour[freq/2+2])
names<-V(g)$name
ids<-1:length(names)
names(ids)<-names
from<-as.character(edge[,1])
to<-as.character(edge[,2])
edges<-matrix(c(ids[from], ids[to]), nc=2)
my_weight<-edge[,3]
g<-add.edges(g, t(edges), weight=my_weight)
V(g)$label<-V(g)$name
my_radius<-sqrt(my_verts[,2]/pi)
V(g)$size<-my_radius
V(g)$label.cex<-0.0001
del_ids<-intersect(which(degree(g)==0), which(freq==1))
g1<-delete.vertices(g, ids[del_ids])
length(del_ids)
jpeg(file="BC9.jpeg", height=7016, width=7016, res=600)
par(mfrow=c(1,1), mar=c(5,5,5,5))
title=c("BC9")
layout<-layout_with_graphopt(g1, niter=800)
plot(g1, layout=layout, edge.color="darkblue", main=title, edge.width=0.2)
dev.off()
This currently will plot most nodes as independent points but some nodes get plotted on top of each other. Is there a way to get the nodes to have better spacing?
Thanks.
I have the same problem with igraph, the layouts these algorithms provide don't seem to prevent overlap of nodes.
Someone will probably come up with a good igraph-only solution but my very tedious current workaround for this is: I open the network on Gephi, then I use Force Atlas 2 algorithm with the "prevent overlap" option checked, I save the .gexf file, then later I extract the x,y,z coordinates from the file, then I use it as the layout in igraph.
From what I understand, the coordinates for the plots are rescaled, but you can stop that with the rescale argument and do things manually. You can also use the norm_coords() to normalize the plot with the boundaries you have. I don't understand all the detail in this, but it works for me.
library(igraph)
g <- barabasi.game(100) # create a graph
lo <- layout_with_kk(g) # create a layout
lo <- norm_coords(lo, ymin=-1, ymax=1, xmin=-1, xmax=1)
# I think this tells igraph to normalize the coordinates of the
# layout relative to the space you're working with
# see how it works
par(mfrow=c(1,2), mar=c(0,0,0,0))
plot(g, edge.arrow.width = .25,
edge.arrow.size = .25,
vertex.label = NA,
vertex.size = 5,
rescale=FALSE,
layout=lo*0.25)
plot(g, edge.arrow.width = .25,
edge.arrow.size = .25,
vertex.label = NA,
vertex.size = 5,
rescale=FALSE,
layout=lo*1)
Created on 2020-09-10 by the reprex package (v0.3.0)

Resources