Search code examples
rmatrixigraphnetwork-analysis

Counting number of viable pathways in a network diagram from a specific node input


small network example

I'm trying to identify and count all non-zero pathways starting from a specific node in a network (here N2). Each step through a node is 1, so from N2 to Z3 would be a length of 2. I'd like to extract each possible pathway as rows with node ID in separate columns, and have a count as well. This is the matrix from the above diagram and as far as I got:

mat1 <- read.table(text = "
  X1 N1 N2 A2 Z3  M
1 N1 -1  0 -1  0  0
2 N2  0 -1 -1  0  0
3 A2  1  1  0 -1 -1
4 Z3  0  0  1 -1  0
5  M  0  0  1  0 -1
", header = TRUE)

mat1 <- as.matrix(mat1)

nzpath <- which(mat1 != 0, arr.ind = TRUE)

pathways <- data.frame(
  row = nzpath[, 1],
  column = nzpath[, 2],
  value = mat1[nzpath[, 1], nzpath[, 2]])

the desired output example:

out <- read.table(text = "
  node1 node2 node3 count
1 N2 A2 Z3 2
", header = TRUE)

Solution

  • You might want to find a less convoluted (removing at least some lapply()'s) solution from

    v0

    result = local({
      l <-
        lapply(colnames(mat1), \(v) igraph::all_simple_paths(g, v, mode = "out")) |>
        unlist(recursive = FALSE) |>
        lapply(\(x) names(x))
    
      n <- lengths(l)
      m <- max(n)
      l <- lapply(l, `length<-`, m)
    
      data.frame(
        do.call("rbind", l) |>
          data.frame() |>
          setNames(c("from", paste0("to", seq(m-1)))),
        count = n - 1,
        row.names = NULL
      )
    })
    

    giving

    > result 
      from to1  to2 count
    1   N1  A2 <NA>     1
    2   N1  A2    M     2
    3   N1  A2   Z3     2
    4   N2  A2 <NA>     1
    5   N2  A2    M     2
    6   N2  A2   Z3     2
    7   A2   M <NA>     1
    8   A2  Z3 <NA>     1
    

    Corrected input:

    mat1 <- matrix(
      c(
        1, 0, 0, 0, 0,
        0, 1, 0, 0, 0,
        0, 0, 1, 0, 1,
        0, 0, 0, 1, 1,
        1, 1, 0, 0, 0
      ),
      nrow = 5, ncol = 5, byrow = TRUE, 
      dimnames = list(NULL, c("M", "Z3", "N1", "N2", "A2"))
    )
    g <- igraph::graph_from_adjacency_matrix(mat1, mode = "directed")
    

    One advantage of {base} + {igraph} is that we do not need to care about changes in, say, {tidyverse}, which has alternatives to the advertised base functions.