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
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 |>)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With