Change Layout Structure in IGraph Plot based on Community - r

I created an igraph with a community membership identified:
fc <- fastgreedy.community(graph)
colors <- rainbow(max(membership(fc)))
This provided me the clusters that each of the nodes belong to.
Now when I plot this:
plot(graph,vertex.color=colors[membership(fc)],
layout=layout.kamada.kawai)
it doesn't provide a layout where it exclusively separates each group of nodes based on the membership. Does anyone know a different layout that can provide this? All this is doing is taking the layout: kamada.kawai and coloring in the memberships rather than restructuring the layout so that it is organized by membership.
Hope this question makes sense. Thanks!

You have to calculate the Kamada-Kawai layout with an artificial weight vector that assigns a high weight to edges within clusters and a low weight to edges that cross cluster boundaries:
> graph <- grg.game(100, 0.2) # example graph
> cl <- fastgreedy.community(graph)
> weights <- ifelse(crossing(cl, graph), 1, 100)
> layout <- layout_with_kk(graph, weights=weights)
> plot(graph, layout=layout)
The trick here is the ifelse(crossing(cl, graph), 1, 100) part -- crossing(cl, graph) takes a clustering and the graph that the clustering belongs to, and returns a Boolean vector that defines for each edge whether the edge is crossing cluster boundaries or not. The ifelse() call then simply replaces TRUE (i.e. edge crossing boundaries) in this vector with 1 and FALSE (i.e. edge stays within the cluster) with 0.

Related

Change edge size in igraph

I want to plot a simple star graph in which the size of the edges depends on a score representing a difference of perception between the central node (e.g.,a leader) and the other nodes (e.g., its employees).
I succeeded in modifying the colors, the size of the node, the width of the edges but not the size of the latter.
How would you do?
library(igraph)
nodes <- read.csv("exemple_nodes.csv", header=T, as.is=T)
links <- read.csv("exemple_edges.csv", header=T, as.is=T)
st <- graph_from_data_frame(d=links, vertices=nodes, directed=T)
plot(st, vertex.color=V(st)$perception.type)
With the ggraph package and one of the geom_edge_ func' (e.g., geom_edge_arc, geom_edge_diagonal), in order to use the edge_width parameter, depending on a numeric value associated with the edges, in the edges-list (hereafter "value"). For example:
ggraph::ggraph(st) +
ggraph::geom_edge_diagonal(aes(edge_width = as.numeric(value)) )
In addition, ggraph allow you to specify other edges-parameters inside the geom_edge_ func', for example edge_alpha = as.numeric(value).
I think that what you want is to position the vertices so that you can control the length of the edges. If that is not what you want, then please explain what you mean by the "size" of the edges.
You do not provide your data so that we cannot use exactly your graph. I will use a generic star graph as an example. In order to control the placement of the vertices, you need to use the layout parameter. The basic function layout_as_star will place the first vertex at the center and the other vertices equally spaced around it at the same distance. Because this layout function places the center vertex at (0,0) and the remaining nodes on a unit circle around the center, it is easy to adjust it so that the distance of the outer vertices is controlled by a parameter. Just multiply the coordinates by the parameter and it will proportionally change the distance. I just make something up for the distances, but you can use your parameter.
## Make up perception parameter
set.seed(271828)
Perception = sample(4, 9, replace=T)
Perception
[1] 2 3 4 4 1 4 2 2 1
Now there is one weight for every outer vertex, but we need a weight for the central vertex. We don't want it to move so we use a weight of 1.
Weight = c(1, Perception)
LO = layout_as_star(S10)
LO = LO*Weight
plot(S10, layout=LO)

R, igraph: ordered layout

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)])

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

(igraph) Grouped layout based on attribute

I'm using the iGraph package in R to layout a network graph, and I would like to group the vertex coordinates based on attribute values.
Similar to the answered question How to make grouped layout in igraph?, my question differs in that the nodes needn't be grouped by a community membership that was derived from a community detection algorithm.
Rather, I want to layout with groups based on attribute values that are known in advance for each vertex.
For example, if each vertex has an attribute "Master.Org", and there are ~10 to ~20 distinct values for Master.Org, then how can I layout the graph such that all vertices within the same Master.Org are grouped ?
Thanks!
Additional Detail
In fact, two separate attributes provide nested levels of grouping.
My goal is to layout a graph object such that the "Master.Org" and "Org.Of" values are grouped together in their XY coordinates on the graph.
For example, each node will belong to an "Org.Of". And there can be multiple "Org.Of" values within the "Master.Org".
Thoughts ?
Thanks!
While this question is rather old, it is a reasonable question and deserves an answer.
No data was provided so I will generate an arbitrary example.
library(igraph)
set.seed(1234)
G = erdos.renyi.game(20, 0.25)
V(G)$Group1 = sample(3,20, replace=TRUE)
plot(G, vertex.color=rainbow(3, alpha=0.4)[V(G)$Group1])
Without doing anything, the Group is ignored.
Now, we need to create a layout that will plot nodes
in the same group close together. We can do this by creating
a graph with the same nodes, but with additional links between
nodes in the same group. The within-group links will be given
a high weight and the original links will be given a small weight.
This will cluster nodes in the same group. We then apply the
layout to plotting the original graph, without the extra links.
They were just to get a good layout.
G_Grouped = G
E(G_Grouped)$weight = 1
## Add edges with high weight between all nodes in the same group
for(i in unique(V(G)$Group1)) {
GroupV = which(V(G)$Group1 == i)
G_Grouped = add_edges(G_Grouped, combn(GroupV, 2), attr=list(weight=5))
}
## Now create a layout based on G_Grouped
set.seed(567)
LO = layout_with_fr(G_Grouped)
## Use the layout to plot the original graph
plot(G, vertex.color=rainbow(3, alpha=0.4)[V(G)$Group1], layout=LO)
If you want to go beyond this to have multiple levels of grouping, just add additional links with appropriate weights to connect the subgroups too.

After clustering in R (iGraph, etc), can you maintain nodes+edges from a cluster to do individual cluster analysis?

Basically I have tried a few different ways of clustering. I can usually get to a point in iGraph where each node is labeled with a cluster. I can then identify all the nodes within a single cluster. However, this loses their edges.
I'd have to re-iterate back over the original dataset for all the nodes in cluster 1 to get only those where both nodes+the edge are within the cluster. I'd have to do this for every cluster.
This seems like a painfully long process and there is probably a shortcut my google-fu is missing.
So, is there an easy way to, after clustering or performing community detection processes, to maintain an individual cluster/community as its own smaller graph -- that is, retaining all nodes AND edges between them?
You can use delete.vertices() to create a subgraph. Example:
library(igraph)
set.seed(123)
# create random graph
g <- barabasi.game(100, directed = F)
plot(g, layout=layout.fruchterman.reingold)
# do community detection
wc <- multilevel.community(g)
V(g)$community <- membership(wc)
# make community 1 subgraph
g_sub <- delete.vertices(g, V(g)[community != 1])
plot(g_sub, layout=layout.fruchterman.reingold)
An alternative:
#Create random network
d <- sample_gnm(n=50,m=40)
#Identify the communities
dc <- cluster_walktrap(d)
#Induce a subgraph out of the first community
dc_1 <- induced.subgraph(d,dc[[1]])
#plot that specific community
plot(dc_1)

Resources