rggraphtidygraph

Calculate degree for subgraphs with tidygraph


my problem is the following. I have a network with multiple layers (based on edge attributes), like year in the highschool network.

I would like to store node degree for each layer as a separate node attribute, to use it in visualisation.

While facet_edges is a great and easy way of showing state-specific interaction differences (i.e. the interactions in 1957 and 1958 in case of the highschool network), when you want to highlight the consequence of these interaction differences, for example the degree, it naturally does not get re-calculated for the subgraphs.

gr <- as_tbl_graph(highschool)
gr <- gr |> mutate(d = centrality_degree())
ggraph(gr, layout = "stress")  + 
  geom_edge_link() +
  geom_node_point(aes(size = d)) +
  facet_edges(~year)

enter image description here

This is related to this question Small multiples plots replicating entire network in each panel in ggraph() / this issue https://github.com/thomasp85/ggraph/issues/343, as goal is to visualise the changes like so (feat1 and feat2 are not the degree of course, but might as well be):

gr <- as_tbl_graph(highschool) |>
  mutate(
    feat1 = sample(1:3, n(), replace = TRUE),
    feat2 = sample(1:8, n(), replace = TRUE)
  ) |>
  mutate_at(vars(feat1, feat2), as.factor)

p1 <- ggraph(gr) +
  geom_edge_link(alpha = 0.1) +
  geom_node_point(aes(size = feat1)) +
  scale_color_brewer(type = "qual") +
  theme_graph()
#> Using "stress" as default layout

p2 <- ggraph(gr) +
  geom_edge_link(alpha = 0.1) +
  geom_node_point(aes(size = feat2)) +
  scale_color_brewer(type = "qual") +
  theme_graph()
#> Using "stress" as default layout

p1 + p2

subgraph degree

I have not found a good way to do this yet. One way to do this is to split the layers apart into separate networks and visualise them separately, but in that case the node positions will not be mapped, and it becomes more difficult to compare the networks.

I attempted to calculate the degree for each layer separately by morphing it, but I'm stuck.

gr |> activate(edges) |> morph(to_split,year) |> crystallise()
Splitting by edges
# A tibble: 2 × 2
  name       graph     
  <chr>      <list>    
1 year: 1957 <tbl_grph>
2 year: 1958 <tbl_grph>

My plan was to calculate the degree with mutate for each subgraph in the graph column above, and then save them as separate node columns for each layer using pivot_wider. This would (in theory) replicate the feat1 and feat2 columns in the example above

gr |> 
activate(edges) |> 
morph(to_split,year) |> 
activate(nodes) |> 
mutate(d = centrality_degree()) |> 
crystallise()

However, I'm getting the following error:

Splitting by edges
Error in `private$check()`:
! This function should not be called directly

Has anyone encountered this before? Does anyone have a better idea to solve this?


Solution

  • You're so close, you need to unmorph(), then activate() edges, then morph() and crystallise() see the reprex below:

    suppressPackageStartupMessages({
      library(dplyr)
      library(ggplot2)
      library(ggraph)
      library(tidygraph)
    })
    
    
    gr <- as_tbl_graph(highschool) |>
      mutate(
        feat1 = sample(1:3, n(), replace = TRUE),
        feat2 = sample(1:8, n(), replace = TRUE)
      ) |>
      mutate_at(vars(feat1, feat2), as.factor)
    
    
    gr |> 
      activate(edges) |> 
      morph(to_split, year) |> 
      activate(nodes) |> 
      mutate(deg = centrality_degree()) |> 
      unmorph() |>
      activate(edges) |>
      morph(to_split, year) |> 
      crystallise()
    
    #> Splitting by edges
    #> Splitting by edges
    #> # A tibble: 2 × 2
    #>   name       graph     
    #>   <chr>      <list>    
    #> 1 year: 1957 <tbl_grph>
    #> 2 year: 1958 <tbl_grph>
    

    Created on 2023-08-16 with reprex v2.0.2

    To extract metrics you could use purrr::map() to iterate over the graph column.