Search code examples
rigraph

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

  • 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