I'm using igraph
function to create a tripartite graph linking an instructor (A
) to a list of students (B
to R
) who participate in the 3 clubs mentored by this instructor. Student have crossed memberships in 2 classes and may have the same gender. Finally, the width of the edges represents the amount of time the instructor and each student dedicate, on average, to each club in a given week.
Creating the graph is straightforward (my code is provided at below), but given that my list contains a total of 17 students (B
to R
), it would be better to present the graph in a horizontal way by placing the instructor (A
) on top, the 3 club in the middle, with the 17 students (B
to R
) on the bottom. I suspect this is because I used layout_with_sugiyama()
for my graph, but could anyone suggest an alternative to achieve my desired horizontal layout?
Below is my current R
code for this graph:
rm(list=ls())
library(foreign)
library(igraph)
library(dplyr)
### create tripartite node list and pairwise attributes
time <- data.frame(student = c("A", "A", "A", "B", "B", "B", "C", "C", "C", "D", "D", "D", "E", "E", "E", "F", "F", "F", "G", "G", "G", "H", "H", "H", "I", "I", "I", "J", "J", "J", "K", "K", "K", "L", "L", "L", "M", "M", "M", "N", "N", "N", "O", "O", "O", "P", "P", "P", "Q", "Q", "Q", "R", "R", "R"),
club = c("club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3", "club 1", "club 2", "club 3"),
hours = c(10, 3, 6, 5, 2, 1, 3, 3, 2, 7, 5, 11, 1, 0, 3, 8, 2, 2, 2, 2, 0, 5, 7, 11, 1, 0, 1, 0, 1, 3, 8, 9, 2, 0, 0, 3, 4, 3, 6, 3, 1, 0, 3, 1, 7, 0, 0, 1, 0, 1, 5, 1, 3, 3))
### convert time dataframe into a graph object
df <- time[!time$hours == 0, ]
g <- graph_from_data_frame(df, directed = FALSE)
E(g)$width <- log(E(g)$hours)
### parse the data into three disjoint sets, use different node shapes to distinguish them
A <- "A"
club <- c("club 1", "club 2", "club 3")
V(g)$type <- 1
V(g)[name %in% club]$type <- 2
V(g)[name %in% "A"]$type <- 3
shape <- c("circle", "square", "circle")
size <- c(12, 15, 12)
### label class affiliation (except node A; G, K, L, Q do not belong to any classes)
Class1 <- c("B", "C", "E", "H", "J", "O")
Class2 <- c("D", "F", "M", "P", "I", "N", "R")
V(g)$color[V(g)$name] = "white"
V(g)$color[V(g)$name %in% Class1] = "red"
V(g)$color[V(g)$name %in% Class2] = "orange"
V(g)$color[V(g)$name == "A"] = "olivedrab1"
### highlight same sex nodes
s <- c("B", "D", "F", "G", "H", "K", "M", "P", "Q")
s_col = ifelse(V(g)$name %in% s,'black','grey80')
layout = layout_with_sugiyama(g, layers=V(g)$type)
V(g)$vertex_degree <- igraph::degree(g)
plot(g,
layout=cbind(V(g)$type, layout$layout[,1]), edge.curved=0,
vertex.color = V(g)$color,
vertex.label.color = "black",
vertex.label.cex = 0.45,
vertex.size = size[V(g)$type],
vertex.shape = shape[V(g)$type],
vertex.frame.color = s_col,
edge.color= "grey30",
asp = 1.3,
edge.width = E(g)$width
)
The above code generates this graph.
Yet my desired output should look something like this
Thanks for the clarification. This is more meant as a comment than an answer. If I run the code as proposed by yourself and commenters here:
plot(g,
layout=cbind(V(g)$type, layout$layout[,1])[,2:1], edge.curved=0,
vertex.color = V(g)$color,
vertex.label.color = "black",
vertex.label.cex = 0.45,
vertex.size = size[V(g)$type],
vertex.shape = shape[V(g)$type],
vertex.frame.color = s_col,
edge.color= "grey30",
asp = 1.3,
edge.width = E(g)$width
)
How is that different from what you trying to accomplish?
EDIT: One way to find a nicer looking distribution of the vertices on the x-axes is to use the cut
function:
idx <- which(layout$layout[,2] == 2) # find "club"-vertices
cuts <- layout$layout[idx, 1] # find x-coords of vertices
cut(cuts, length(idx)) # cut into 3 intervals
layout$layout[idx,1] <- c(6,7.5,9) # manually calculated even spans between x-coords
However, I am sure that there are better ways to do this.