Define layout for igraph layout_with_sugiyama from vertice attribute - r

It's my first time working with igraph - so please excuse my basic questions. I'm doing a stakeholder analysis and have different actors on different administrative scales.
I'm trying to plot my network in a hierarchical way, because I want to show the interaction between administrative boundaries.
My vertices have the attributes "name", "Label" and "Scale".
I want to plot the graph with Scale as the layers.
edges <- data.table::fread("Master_edges.csv")
nodes <- data.table::fread("Master_nodes.csv")
V(g1_2)$name <- nodes$Label
V(g1_2)$Scale <-nodes$Scale
l <- layout_with_sugiyama(layers=V(g1_2)$Scale) #this is wrong, how do I define the layers?
plot(g1_2, layout=l, vertex.label=V(g1_2), vertex.color=NA)
Also I have different types of edges - how can I plot the different edge-types in different plots?

Related

How to scale labels in network graph based on "importance"?

For this project on network analysis, I'm studying communities between characters in Game Of Thrones using igraph and a handful of algorithms (walktrap, spinglass, etc). If all the labels are turned on, the plot becomes way too crowded. Is there a way to scale nodes and their labels based on the weight of their edges?
The only documentation I've been able to find is for the global resizing of labels, regardless of node importance. These are pretty simple communities -- Nodes are the names of characters, Edges are the source and target characters and the weight of each relationship.
# Walktrap algorithm
book5_walktrap = walktrap.community(book5, weights=E(book5)$weight)
plot(book5_walktrap, book5, vertex.label = NA, vertex.size=2, main="Walktrap")
# Or vertex.label = nodes$Label
Ideally, I'd like for only the most prominent characters of each community to be blown up.
You can set the size of the label using vertex.label.cex. You do not provide data nor how you are measuring importance, so I illustrate with the karate data set from igraphdata. I simply use a scaled version of degree as the importance of a node.
library(igraph)
library(igraphdata)
data(karate)
karate_walktrap = walktrap.community(karate)
set.seed(1234)
plot(karate_walktrap, karate, vertex.size=2,
main="Walktrap", vertex.label.cex=round(degree(karate)/5))

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.

revealing clusters of interaction in igraph

I have an interaction network and I used the following code to make an adjacency matrix and subsequently calculate the dissimilarity between the nodes of the network and then cluster them to form modules:
ADJ1=abs(adjacent-mat)^6
dissADJ1<-1-ADJ1
hierADJ<-hclust(as.dist(dissADJ1), method = "average")
Now I would like those modules to appear when I plot the igraph.
g<-simplify(graph_from_adjacency_matrix(adjacent-mat, weighted=T))
plot.igraph(g)
However the only thing that I have found thus far to translate hclust output to graph is as per the following tutorial: http://gastonsanchez.com/resources/2014/07/05/Pretty-tree-graph/
phylo_tree = as.phylo(hierADJ)
graph_edges = phylo_tree$edge
graph_net = graph.edgelist(graph_edges)
plot(graph_net)
which is useful for hierarchical lineage but rather I just want the nodes that closely interact to cluster as follows:
Can anyone recommend how to use a command such as components from igraph to get these clusters to show?
igraph provides a bunch of different layout algorithms which are used to place nodes in the plot.
A good one to start with for a weighted network like this is the force-directed layout (implemented by layout.fruchterman.reingold in igraph).
Below is a example of using the force-directed layout using some simple simulated data.
First, we create some mock data and clusters, along with some "noise" to make it more realistic:
library('dplyr')
library('igraph')
library('RColorBrewer')
set.seed(1)
# generate a couple clusters
nodes_per_cluster <- 30
n <- 10
nvals <- nodes_per_cluster * n
# cluster 1 (increasing)
cluster1 <- matrix(rep((1:n)/4, nodes_per_cluster) +
rnorm(nvals, sd=1),
nrow=nodes_per_cluster, byrow=TRUE)
# cluster 2 (decreasing)
cluster2 <- matrix(rep((n:1)/4, nodes_per_cluster) +
rnorm(nvals, sd=1),
nrow=nodes_per_cluster, byrow=TRUE)
# noise cluster
noise <- matrix(sample(1:2, nvals, replace=TRUE) +
rnorm(nvals, sd=1.5),
nrow=nodes_per_cluster, byrow=TRUE)
dat <- rbind(cluster1, cluster2, noise)
colnames(dat) <- paste0('n', 1:n)
rownames(dat) <- c(paste0('cluster1_', 1:nodes_per_cluster),
paste0('cluster2_', 1:nodes_per_cluster),
paste0('noise_', 1:nodes_per_cluster))
Next, we can use Pearson correlation to construct our adjacency matrix:
# create correlation matrix
cor_mat <- cor(t(dat))
# shift to [0,1] to separate positive and negative correlations
adj_mat <- (cor_mat + 1) / 2
# get rid of low correlations and self-loops
adj_mat <- adj_mat^3
adj_mat[adj_mat < 0.5] <- 0
diag(adj_mat) <- 0
Cluster the data using hclust and cutree:
# convert to dissimilarity matrix and cluster using hclust
dissim_mat <- 1 - adj_mat
dend <- dissim_mat %>%
as.dist %>%
hclust
clusters = cutree(dend, h=0.65)
# color the nodes
pal = colorRampPalette(brewer.pal(11,"Spectral"))(length(unique(clusters)))
node_colors <- pal[clusters]
Finally, create an igraph graph from the adjacency matrix and plot it using the fruchterman.reingold layout:
# create graph
g <- graph.adjacency(adj_mat, mode='undirected', weighted=TRUE)
# set node color and plot using a force-directed layout (fruchterman-reingold)
V(g)$color <- node_colors
coords_fr = layout.fruchterman.reingold(g, weights=E(g)$weight)
# igraph plot options
igraph.options(vertex.size=8, edge.width=0.75)
# plot network
plot(g, layout=coords_fr, vertex.color=V(g)$color)
In the above code, I generated two "clusters" of correlated rows, and a third group of "noise".
Hierarchical clustering (hclust + cuttree) is used to assign the data points to clusters, and they are colored based on cluster membership.
The result looks like this:
For some more examples of clustering and plotting graphs with igraph, checkout: http://michael.hahsler.net/SMU/LearnROnYourOwn/code/igraph.html
You haven't shared some toy data for us to play with and suggest improvements to code, but your question states that you are only interested in plotting your clusters distinctly - that is, graphical presentation.
Although igraph comes with some nice force directed layout algorithms, such as layout.fruchterman.reingold, layout_with_kk, etc., they can, in presence of a large number of nodes, quickly become difficult to interpret and make sense of at all.
Like this:
With these traditional methods of visualising networks,
the layout algorithms, rather than the data, determine the visualisation
similar networks may end up being visualised very differently
large number of nodes will make the visualisation difficult to interpret
Instead, I find Hive Plots to be better at displaying important network properties, which, in your instance, are the cluster and the edges.
In your case, you can:
Plot each cluster on a different straight line
order the placement of nodes intelligently, so that nodes with certain properties are placed at the very end or start of each straight line
Colour the edges to identify direction of edge
To achieve this you will need to:
use the ggnetwork package to turn your igraph object into a dataframe
map your clusters to the nodes present in this dataframe
generate coordinates for the straight lines and map these to each cluster
use ggplot to visualise
There is also a hiveR package in R, should you wish to use a packaged solution. You might also find another visualisation technique for graphs very useful: BioFabric

Plot communities with igraph

I want to create a graph plot with each community of nodes been covered by some background color, similar to the graph by the following code
karate <- graph.famous("Zachary")
wc <- walktrap.community(karate)
modularity(wc)
membership(wc)
plot(wc, karate)
But different from this approach, I want to: (1) group the nodes by myself, instead of resulting from community detection algorithm. I achieved this by wc$membership <- some_vector; (2) plot such graph possibly with overlap between communities, then how can I assign one node to multiple communities?
Plot the graph itself instead of the community structure, and use an appropriately constructed mark.groups argument to plot() to tell igraph which groups should be enclosed by the same group. The following quote is from the manual page of plot.igraph:
mark.groups: A list of vertex id vectors. It is interpreted as a set of vertex groups. Each vertex group is highlighted, by plotting a colored smoothed polygon around and "under" it.

Force-directed graph drawing: Edit the force between specific nodes (R)

I want to analyse a social network using the R packages statnet and/or igraph in reference to force-directed graph drawing (kamada.kawai/fruchterman.reingold). I wounder, if it is possible to adjust the "force" between 2 specific nodes, e.g. to consider a larger or smaller cooperation between 2 stakeholders. However, i do not want to edit the general force between all nodes
(as proposed here:)
How do I lengthen edges in an igraph network plot (layout=fruchterman.reingold)?
The idea on this would be to get a more realistic image of a social network, also for further analysis.
Thanks a lot and nice weekend to everybody!
This layout algorithm supports edge weights, which are basically used as multipliers for the attraction forces along the edges. I.e. edges with high weight will tend to be shorter. Here is a simple example
library(igraph)
g <- graph.ring(10)
# Edge weights, will be recycled
E(g)$weight <- c(1,4)
coords <- layout.fruchterman.reingold(g, weights=E(g)$weight)
# Eliminate the margin
par(mar=c(0,0,0,0))
plot(g, layout=coords, vertex.color="#E495A5", vertex.size=20)

Resources