Как сделать сгруппированный макет в igraph?

В igraph, после применения алгоритма модуляции для нахождения графовых коммунизмов, я хотел бы нарисовать сетевой макет, который четко отображает различные сообщества и их соединения. Что-то вроде "макета атрибутов групп" в Cytoscape: я хочу показать членам каждой группы/сообщества близко друг к другу и сохранить некоторое расстояние между группами/сообществами. Я не мог найти какую-либо функцию в igraph, предоставляя эту функцию из коробки. При публикации этого вопроса я уже нашел простое решение d.i.y, я собираюсь опубликовать его в качестве ответа. Но мне интересно, есть ли какая-либо лучшая возможность или более сложное решение?

Ответ 1

Чтобы расширить предложение Gabor, я создал эту функцию:

weight.community=function(row,membership,weigth.within,weight.between){
if(as.numeric(membership[which(names(membership)==row[1])])==as.numeric(membership[which(names(membership)==row[2])])){
weight=weigth.within
}else{
weight=weight.between
}
return(weight)
}

Просто примените его по строкам матрицы ребер вашего графа (задано get.edgelist(your_graph)) для установки новых весов ребер (членство является вектором принадлежности из результата любого алгоритма обнаружения сообщества):

E(g)$weight=apply(get.edgelist(g),1,weight.community,membership,10,1)

Затем просто используйте алгоритм компоновки, который принимает весы ребер, такие как fruchterman.reingold, как было предложено Габор. Вы можете настроить аргументы веса, чтобы получить нужный граф. Например:

E(g)$weight=apply(get.edgelist(g),1,weight.community,membership,10,1)
g$layout=layout.fruchterman.reingold(g,weights=E(g)$weight)
plot(g)

enter image description here

E(g)$weight=apply(get.edgelist(g),1,weight.community,membership,1000,1)
g$layout=layout.fruchterman.reingold(g,weights=E(g)$weight)
plot(g)

enter image description here

Примечание 1: прозрачность/цвета ребер - это другие параметры моих графиков. У меня есть цветные узлы сообщества, чтобы показать, что он действительно работает.

Примечание 2: используйте membership(comm), а не comm$membership, где comm является результатом алгоритма обнаружения сообщества (например, comm=leading.eigenvector.community(g)). Причина в том, что в первом случае вы получаете числовой вектор с именами (мы хотим), а во втором случае - тот же вектор без имен.

Чтобы получить согласие с несколькими алгоритмами обнаружения сообщества, см. эту функцию .

Ответ 2

Вдохновленный предложением Антуана, я создал эту функцию:

edge.weights <- function(community, network, weight.within = 100, weight.between = 1) {
bridges <- crossing(communities = community, graph = network)
weights <- ifelse(test = bridges, yes = weight.between, no = weight.within)
return(weights) 
}

Функция делает то же самое; просто поместите объект сообщества в слот сообщества, ваш график в сетевом. Я оставил бы weight.between = 1 и настроил значение weight.within.

Затем переносите веса в слот weight в узлах:

E(graph)$weight <- edge.weights(community, graph)

Наконец, используйте алгоритм компоновки, который использует вес, например layout_with_fr (новое имя fruchterman.reingold в igraph 1.0.1).

В качестве примера я использую сеть клубов каратэ в Захари.

library(igraph)
library(igraphdata)
#I load the network
data(karate)
#for reproducible purposes
set.seed(23548723)
karateLayout <- layout_with_fr(karate)
par(mar = c(0,0,2,0))
plot(karate, vertex.size = 10, vertex.color = "steelblue4", edge.width = 1, 
vertex.label = NA, edge.color = "darkgrey", layout = karateLayout,
main = "Zachary karate club network" )

введите описание изображения здесь

Я обнаруживаю сообщества путем многоуровневой оптимизации модульности с помощью функции cluster_louvain:

Communitykarate <- cluster_louvain(karate)

Далее это личное предпочтение по умолчанию:

prettyColors <- c("turquoise4", "azure4", "olivedrab","deeppink4")
communityColors <- prettyColors[membership(Communitykarate)]

График с сообществами, выделенными с использованием цветов:

plot(x = Communitykarate, y = karate, edge.width = 1, vertex.size = 10, 
vertex.label = NA, mark.groups = NULL, layout = karateLayout, col = communityColors,
main = "Communities in Zachary karate club network",
edge.color = c("darkgrey","tomato2")crossing(Communitykarate, karate) + 1])

введите описание изображения здесь

Теперь, смысл, почему этот вопрос существует.

E(karate)$weight <- edge.weights(Communitykarate, karate)
# I use the original layout as a base for the new one
karateLayoutA <- layout_with_fr(karate, karateLayout)
# the graph with the nodes grouped
plot(x = Communitykarate, y = karate, edge.width = 1, vertex.size = 10, 
mark.groups = NULL, layout = karateLayoutA, vertex.label = NA, col = communityColors, 
c("darkgrey","tomato2")[crossing(Communitykarate, karate) + 1],
main = "Communities in Zachary karate club network (grouped)")

введите описание изображения здесь

Если вы попробуете больше веса, у вас будет:

E(karate)$weight <- edge.weights(Communitykarate, karate, weight.within = 1000)
karateLayoutB <- layout_with_fr(karate, karateLayout)
plot(x = Communitykarate, y = karate, edge.width = 1, vertex.size = 10,
 mark.groups = NULL, layout = karateLayoutB, vertex.label = NA, col = communityColors, 
c("darkgrey","tomato2")[crossing(Communitykarate, karate) + 1],
main = "Communities in Zachary karate club network (grouped)")

введите описание изображения здесь

Ответ 3

Функция layout.modular предоставляет сгруппированный макет для графика из результата любого метода обнаружения сообщества igraph:

c <- fastgreedy.community(G)

layout.modular <- function(G,c){
nm <- length(levels(as.factor(c$membership)))
gr <- 2
while(gr^2<nm){
    gr <- gr+1
}
i <- j <- 0
for(cc in levels(as.factor(c$membership))){
    F <- delete.vertices(G,c$membership!=cc)
    F$layout <- layout.kamada.kawai(F)
    F$layout <- layout.norm(F$layout, i,i+0.5,j,j+0.5)
    G$layout[c$membership==cc,] <- F$layout
    if(i==gr){
        i <- 0
        if(j==gr){
            j <- 0
        }else{
            j <- j+1
        }
    }else{
        i <- i+1
    }
}
return(G$layout)
}

G$layout <- layout.modular(G,c)
V(G)$color <- rainbow(length(levels(as.factor(c$membership))))[c$membership]
plot(G)

Ответ 4

Одним из решений было бы установить вес границ графа на основе модуляции. Установите края внутри модуля на некоторый большой вес, а края между модулями - на небольшой вес. Затем вызовите layout.fruchterman.reingold() или любой алгоритм, поддерживающий весы ребер.

Возможно, вам придется немного поиграть с фактическими значениями веса, потому что это зависит от вашего графика.