Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing off-diagonal elements with a fixed value

I have a square matrix and now I want to replace all its off-diagonal elements with a fixed value. Some approaches how this can be done are discussed in Replacing non-diagonal elements in a matrix in R (hopefully a better asked this time)

For example,

m <- matrix(c(1,2,3,4,3,9,5,5,8),ncol=3)
m[upper.tri(m) | lower.tri(m)] <- 999
m

I am wondering if the step can be performed using dplyr chain rule, something like

library(dplyr)
matrix(c(1,2,3,4,3,9,5,5,8),ncol=3) %>%
 ### add this -> m[upper.tri(m) | lower.tri(m)] <- 999

Any pointer will be very helpful

like image 825
Brian Smith Avatar asked Aug 25 '26 17:08

Brian Smith


2 Answers

If it's for the sake of brevity, you could define + apply an anonymous function in your pipeline like so:

matrix(c(1,2,3,4,3,9,5,5,8),ncol=3) %>%
  (\(.) {.[upper.tri(.) | lower.tri(.)] = 999; .})() %>%
## rest of pipeline

Edit shamelessly stealing borrowing from @Maël 's elegant solution:

matrix(c(1,2,3,4,3,9,5,5,8),ncol=3) %>%
  ifelse(row(.) == col(.), ., 999) %>%
## rest of pipeline

(note: does not work with the native pipe operator |>)

like image 94
I_O Avatar answered Aug 27 '26 07:08

I_O


For large enough m and n, it is much more efficient to initialize the m-by-n result with all elements equal to r and replace only the diagonal elements:

m <- 1000L
n <- 1000L
r <- -1L
x <- diag(seq_len(min(m, n)), m, n)

microbenchmark::microbenchmark(
    A = x |> (\(.) `diag<-`(array(r, dim(.), dimnames(.)), diag(., names = FALSE)))(),
    B = x |> (\(.) `[<-`(., row(.) != col(.), r))(),
    C = x |> (\(.) `[<-`(., upper.tri(.) | lower.tri(.), r))(),
    times = 1000L)
Unit: microseconds
 expr      min         lq       mean    median        uq      max neval
    A   67.445   283.2075   520.3644   315.823   343.498 12723.82  1000
    B 4475.232  5024.3450  5750.4401  5479.527  6081.837 16821.36  1000
    C 8863.134 10034.4835 11035.8599 10535.052 11030.066 22211.34  1000
like image 32
Mikael Jagan Avatar answered Aug 27 '26 07:08

Mikael Jagan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!