2
votes

Following this question, is it possible to join two adjacency matrices, and retain the values of new rows and columns?

Building on the example in the reference, the union of mat1 and mat2 initializes values in newly added rows and columns with 0. I want to initialize them with the value in the adjacency matrix, preserving that information.

> mat1
      Tommy Roy Addy Sam
Tommy     0   1    0  -1
Roy      -1  -1    1   0
Addy      1   0   -1   0
Sam       0   0   -1   1
> mat2
     Mike Roy Addy Sam Dan
Mike    0   1    0  -1   0
Roy    -1  -1    1   0   1
Addy    1   0   -1   0  -1
Sam     0   0   -1   1   0
Dan     1   0    0  -1   1

complete_matrix <- function(mat, ref) {
  dif <- setdiff(rownames(ref), rownames(mat))
  mat <- rbind(mat, matrix(0, length(dif), ncol(mat), dimnames = list(dif, NULL)))
  mat <- cbind(mat, matrix(0, nrow(mat), length(dif), dimnames = list(NULL, dif)))
  return(mat)
}

> complete_matrix(mat2, mat1)
      Mike Roy Addy Sam Dan Tommy
Mike     0   1    0  -1   0     0
Roy     -1  -1    1   0   1     0
Addy     1   0   -1   0  -1     0
Sam      0   0   -1   1   0     0
Dan      1   0    0  -1   1     0
Tommy    0   0    0   0   0     0

For example, I want complete_matrix(mat2, mat1) to yield the following (observe the Tommy row and column):

      Mike Roy Addy Sam Dan Tommy
Mike     0   1    0  -1   0     0
Roy     -1  -1    1   0   1     1
Addy     1   0   -1   0  -1     0
Sam      0   0   -1   1   0    -1
Dan      1   0    0  -1   1     0
Tommy    0   1    0  -1   0     0

dput for c&P:

mat1 <- structure(c(0L, -1L, 1L, 0L, 1L, -1L, 0L, 0L, 0L, 1L, -1L, -1L, 
            -1L, 0L, 0L, 1L), .Dim = c(4L, 4L), .Dimnames = list(c("Tommy", 
                                                                   "Roy", "Addy", "Sam"), c("Tommy", "Roy", "Addy", "Sam")))
mat2 <- structure(c(0L, -1L, 1L, 0L, 1L, 1L, -1L, 0L, 0L, 0L, 0L, 1L, 
                    -1L, -1L, 0L, -1L, 0L, 0L, 1L, -1L, 0L, 1L, -1L, 0L, 1L), .Dim = c(5L, 
                                                                                       5L), .Dimnames = list(c("Mike", "Roy", "Addy", "Sam", "Dan"), 
                                                                                                             c("Mike", "Roy", "Addy", "Sam", "Dan")))
2
What do you want to happen if there is a discrepancy? For example, what it the Addy/Sam value is 1 in mat1 and 0 in mat2? (Or can we assume no discrepancy?) - Gregor Thomas
@Gregor, this is an excellent question. For my purposes, there will never be a discrepancy. This is a small reprex for an enormous adjacency matrix calculation that I'm chunking into manageable pieces, then joining together into one enormous sparse matrix. - Rich Pauloo

2 Answers

3
votes

You could do it using the union function of the igraph package, which would require to convert your matrices to graphs first and then convert back the resulting graph into a matrix:

library(igraph)

g1 = graph_from_adjacency_matrix(mat1,weighted=T)
g2 = graph_from_adjacency_matrix(mat2,weighted=T)
g3 = union(g1,g2)

union doesn't automatically merge the weights of g1 and g2, but keeps them as separate attributes weight_1 and weight_2. We can combine them by taking the minimum value between the two weights, which, if there are no discrepancies between the 2 matrices, is just a way to merge them and remove the NA values.

E(g3)$weight = pmin(E(g3)$weight_1,E(g3)$weight_2,na.rm=T)
res = as.matrix(as_adj(g3,attr="weight"))

      Tommy Roy Addy Sam Mike Dan
Tommy     0   1    0  -1    0   0
Roy      -1  -1    1   0   -1   1
Addy      1   0   -1   0    1  -1
Sam       0   0   -1   1    0   0
Mike      0   1    0  -1    0   0
Dan       0   0    0  -1    1   1
3
votes

I would:

  1. turn the matrices into individual pair-matrices (i.e. pair-value), i.e. matrix to long
  2. bind them,
  3. decide what to do for repeated pairs (here I just take first value...)
  4. then spread to wide back.

Update: would probably run distinct() and add value in: group_by(ind_1, ind_2, value). Plus fill with 0 in spread()?

Example:

library(tidyverse)
mat1 <- structure(c(0L, -1L, 1L, 0L, 1L, -1L, 0L, 0L, 0L, 1L, -1L, -1L, 
                    -1L, 0L, 0L, 1L), .Dim = c(4L, 4L), .Dimnames = list(c("Tommy", 
                                                                           "Roy", "Addy", "Sam"), c("Tommy", "Roy", "Addy", "Sam")))
mat2 <- structure(c(0L, -1L, 1L, 0L, 1L, 1L, -1L, 0L, 0L, 0L, 0L, 1L, 
                    -1L, -1L, 0L, -1L, 0L, 0L, 1L, -1L, 0L, 1L, -1L, 0L, 1L), .Dim = c(5L, 
                                                                                       5L), .Dimnames = list(c("Mike", "Roy", "Addy", "Sam", "Dan"), 
                                                                                                             c("Mike", "Roy", "Addy", "Sam", "Dan")))

mat1_l <- mat1 %>% 
  as.data.frame() %>% 
  rownames_to_column(var = "ind_1") %>% 
  as_tibble() %>% 
  gather(ind_2, value, -ind_1)

mat2_l <- mat2 %>% 
  as.data.frame() %>% 
  rownames_to_column(var = "ind_1") %>% 
  as_tibble() %>% 
  gather(ind_2, value, -ind_1)


rbind(mat1_l, mat2_l) %>% 
  group_by(ind_1, ind_2) %>% 
  slice(1) %>% 
  ungroup() %>% 
  spread(ind_2, value)
#> # A tibble: 6 x 7
#>   ind_1  Addy   Dan  Mike   Roy   Sam Tommy
#>   <chr> <int> <int> <int> <int> <int> <int>
#> 1 Addy     -1    -1     1     0     0     1
#> 2 Dan       0     1     1     0    -1    NA
#> 3 Mike      0     0     0     1    -1    NA
#> 4 Roy       1     1    -1    -1     0    -1
#> 5 Sam      -1     0     0     0     1     0
#> 6 Tommy     0    NA    NA     1    -1     0