1

In a square matrix where only the cells above the diagonal is full (including the diagonal) and the rest is NA, I would like to symmetrically fill the NA's below the diagonal with the values above the diagonal.

Edit: The example may have been misleading, so I slightly modified it.

Example:

library(tidyverse)

Tibble <- tibble(A = c(5, NA, NA),
                 B = c(1, 3, NA),
                 C = c(6, 2, 4))

Tibble

Result:

# A tibble: 3 × 3
      A     B     C
  <dbl> <dbl> <dbl>
1     5     1     6
2    NA     3     2
3    NA    NA     4

Desired outcome:

# A tibble: 3 × 3
      A     B     C
  <dbl> <dbl> <dbl>
1     5     1     6
2     1     3     2
3     6     2     4
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81
Ian
  • 123
  • 5
  • 2
    A simple `tidyr` fill: `df %>% fill(everything(), .direction = "down")` – dcsuka Jul 22 '22 at 17:03
  • 1
    @dcsuka While this technically yields the desired outcome, it's not filling the empty cells symmetrically, but rather filling the empty cells with the value above them. – Ian Jul 22 '22 at 17:09
  • How about this, then: https://stackoverflow.com/questions/18165320/creating-a-symmetric-matrix-in-r – dcsuka Jul 22 '22 at 17:11

5 Answers5

4

With Matrix package:

library(tidyverse)
library(Matrix)

Tibble <- tibble(A = c(5, NA, NA),
       B = c(1, 3, NA),
       C = c(6, 2, 4)) %>% 
  as.matrix()

forceSymmetric(Tibble) 

  A B C
A 5 1 6
B 1 3 2
C 6 2 4

If you want a tibble

forceSymmetric(Tibble) %>% 
  as.matrix() %>% 
  as_tibble()

# A tibble: 3 x 3
      A     B     C
  <dbl> <dbl> <dbl>
1     5     1     6
2     1     3     2
3     6     2     4
Chamkrai
  • 5,912
  • 1
  • 4
  • 14
1

As @onyambu mentioned in the comments, you should first transpose the matrix. Here an example with more 3 rows/columns:

library(tibble)
#> Warning: package 'tibble' was built under R version 4.1.2
Tibble <- tibble(A = c(5, NA, NA, NA),
                 B = c(1, 3, NA, NA),
                 C = c(6, 2, 4, NA),
                 D = c(5, 2, 1, 3))

Tibble_m <- as.matrix(Tibble)
Tibble_m[lower.tri(Tibble_m)] <- t(Tibble_m)[lower.tri(Tibble_m)]
as_tibble(Tibble_m)
#> # A tibble: 4 × 4
#>       A     B     C     D
#>   <dbl> <dbl> <dbl> <dbl>
#> 1     5     1     6     5
#> 2     1     3     2     2
#> 3     6     2     4     1
#> 4     5     2     1     3

Created on 2022-07-22 by the reprex package (v2.0.1)

An option could be first convert your tibble to a matrix and then fill the lower.tri with the upper.tri like this:

library(tibble)
Tibble <- tibble(A = c(5, NA, NA),
                 B = c(1, 3, NA),
                 C = c(6, 2, 4))

Tibble_m <- as.matrix(Tibble)
Tibble_m[lower.tri(Tibble_m)] <- Tibble_m[upper.tri(Tibble_m)]
as_tibble(Tibble_m)
#> # A tibble: 3 × 3
#>       A     B     C
#>   <dbl> <dbl> <dbl>
#> 1     5     1     6
#> 2     1     3     2
#> 3     6     2     4

Created on 2022-07-22 by the reprex package (v2.0.1)

Quinten
  • 35,235
  • 5
  • 20
  • 53
  • You will have to transpose the matrix before extracting the upper triangle values since R is column major. ie this will not work if you had more than 4 columns/rows – Onyambu Jul 22 '22 at 17:20
  • @onyambu, Yes you are right! I added an example with 4 columns/rows. – Quinten Jul 22 '22 at 17:27
0

Replace the NA's with 0 and then add the tranpose after the diagonal is zeroed. as_tibble can be omitted if a data frame result is ok.

library(dplyr)

Tibble %>%
  mutate(across(, coalesce, 0)) %>%
  `+`(`diag<-`(t(.), 0)) %>%
  as_tibble
## # A tibble: 3 × 3
##       A     B     C
##   <dbl> <dbl> <dbl>
## 1     5     1     6
## 2     1     3     2
## 3     6     2     4

Using tibbles or data frames is not the best way to represent symmetric matrices and suggest you use a matrix instead.

library(magrittr)

m <- as.matrix(Tibble)

m %>%
  { replace(., is.na(.), 0) } %>%
  `+`(`diag<-`(t(.), 0))

##      A B C
## [1,] 5 1 6
## [2,] 1 3 2
## [3,] 6 2 4
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
0

Try this with base R for loop

for(i in 1:nrow(Tibble)){
    for(j in 1:ncol(Tibble)){
        if(is.na(Tibble[i,j])) Tibble[i,j] <- Tibble[j,i]
    }
}
  • output
# A tibble: 3 × 3
      A     B     C
  <dbl> <dbl> <dbl>
1     5     1     6
2     1     3     2
3     6     2     4
Mohamed Desouky
  • 4,340
  • 2
  • 4
  • 19
0

You can try ifelse + as.matrix to make it

> Tibble[] <- ifelse(is.na(as.matrix(Tibble)), t(Tibble), as.matrix(Tibble))

> Tibble
# A tibble: 3 × 3
      A     B     C
  <dbl> <dbl> <dbl>
1     5     1     6
2     1     3     2
3     6     2     4

or similarily with replace

> Tibble[] <- replace(as.matrix(Tibble), is.na(as.matrix(Tibble)), t(Tibble)[is.na(as.matrix(Tibble))])

> Tibble
# A tibble: 3 × 3
      A     B     C
  <dbl> <dbl> <dbl>
1     5     1     6
2     1     3     2
3     6     2     4
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81