'Is there a simpler way to return edge values when using contract_vertices() and community functions in igraph?
I have a graph with communities. I would like edge weights for all the edges between one community and all other nodes in the graph. I need the individual weights because I'd like to perform calculations on each edge weight (square them, for what it's worth). I've gotten some distance with this and this but it's really awkward.
This is what I've tried:
library(igraph)
## create example graph
g1 <- graph.full(5)
V(g1)$name <- 1:5
g2 <- graph.full(5)
V(g2)$name <- 6:10
g3 <- graph.ring(5)
V(g3)$name <- 11:15
g <- g1 %du% g2 %du% g3 + edge('1', '6') + edge('1', '11')
E(g)$weight <- c(1:27)
# determine community structure
cl <- cluster_louvain(g)
V(g)$name <- membership(cl)
# contract the graph
cg <- contract.vertices(graph = g,
mapping = membership(cl))
cg2 <- simplify(cg)
contract.vertices() retains edge attributes. To extract edge weights between a community and all other nodes (excluding within community nodes) and perform a bit of calculation on the weights, I can do this:
# edges weights linking community 1 and community 2
E(cg)[ V(cg)[1] %--% V(cg)[2]]$weight
#> [1] 1 2 3 4
# edges weights linking community 1 and community 3
E(cg)[ V(cg)[1] %--% V(cg)[3]]$weight
#> [1] 26
bind_rows(
tibble(one_two_edges = E(cg)[ V(cg)[2] %--% V(cg)[1]]$weight) %>%
mutate(summed_square_edge_weight = one_two_edges^2) %>%
summarize_all(sum) %>%
select(-one_two_edges)
,
tibble(one_three_edges = E(cg)[ V(cg)[1] %--% V(cg)[3]]$weight) %>%
mutate(summed_square_edge_weight = one_three_edges^2) %>%
summarize_all(sum) %>%
select(-one_three_edges)
)
#> # A tibble: 2 × 1
#> summed_square_edge_weight
#> <dbl>
#> 1 30
#> 2 676
I know this is very clunky, but I'm stumped on how to simplify it. What I'm looking for is something like this:
tibble(
community_links = c("com_1_to_com_2", "com_1_to_com_3"),
summed_square_edge_weight = c(30, 676))
#> # A tibble: 2 × 2
#> community_links summed_square_edge_weight
#> <chr> <dbl>
#> 1 com_1_to_com_2 30
#> 2 com_1_to_com_3 676
Suggestions about how to get these results more simply?
Created on 2022-04-23 by the reprex package (v2.0.1)
Solution 1:[1]
You are actually approaching it, and I think combn could help you out there
do.call(
rbind,
combn(
vcount(cg),
2,
function(k) {
data.frame(
community_links = paste0(paste0("com_", k), collapse = "_to_"),
summed_square_edge_weight = sum((E(cg)[V(cg)[k[1]] %--% V(cg)[k[2]]]$weight)^2)
)
},
simplify = FALSE
)
)
which gives
community_links summed_square_edge_weight
1 com_1_to_com_2 30
2 com_1_to_com_3 676
3 com_2_to_com_3 0
Solution 2:[2]
After poking around in igraph a bit more, it turns out this is possible: igraph::get.data.frame() returns an edgelist with the edge weights of the contracted vertices (here coerced with tibble::as_tibble(). Turns out the vertex.attr.comb() argument in the contract.vertices() function needs to be set to something that retains the name vertex attribute assigned earlier. I didn't sum the squares just to show the returned values.
cg <- contract.vertices(graph = g,
mapping = membership(cl),
vertex.attr.comb = "min")
as_tibble(as_long_data_frame(cg),
.name_repair = "universal") %>%
select(from:weight) %>%
filter(from != to) %>%
mutate(sq_weight = weight^2)
#> # A tibble: 5 × 4
#> from to weight sq_weight
#> <dbl> <dbl> <dbl> <dbl>
#> 1 1 2 1 1
#> 2 1 2 2 4
#> 3 1 2 3 9
#> 4 1 2 4 16
#> 5 1 3 26 676
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ThomasIsCoding |
| Solution 2 |
