How to find the sum of all anti-diagonals? - r

I have a matrix M:
n = 3
x=c(0.85, 0.1, 0.05)
M <- matrix(NA, n, n);
for(i in 1:n){
for(j in 1:n){
M[i,j] = x[i] * x[j]
}}
# [,1] [,2] [,3]
# [1,] 0.7225 0.085 0.0425
# [2,] 0.0850 0.010 0.0050
# [3,] 0.0425 0.005 0.0025
I need to find the sum of all anti-diagonals include M[1,1] and M[n, n]. My attemp is
d <-matrix(c(0, 1, 2, 1, 2, 3, 2, 3, 4), n)
tapply(M, d, sum)
0 1 2 3 4
0.7225 0.1700 0.0950 0.0100 0.0025
The result is correct for me.
Question. How to define the entries of matrix d? May be as function over col(M) and row(M).

As you mention in your question, row(M) and col(M) can be used, although they start rows/columns at 1 rather than zero, so you need to subtract 2 (1 for each) giving:
tapply(M, row(M) + col(M) - 2, sum)
# 0 1 2 3 4
#0.7225 0.1700 0.0950 0.0100 0.0025

First note that outer can produce the matrix d without explicitly listing its elements.
matrix(c(0, 1, 2, 1, 2, 3, 2, 3, 4), 3)
#> [,1] [,2] [,3]
#> [1,] 0 1 2
#> [2,] 1 2 3
#> [3,] 2 3 4
outer(0:2, 0:2, `+`)
#> [,1] [,2] [,3]
#> [1,] 0 1 2
#> [2,] 1 2 3
#> [3,] 2 3 4
Created on 2022-03-24 by the reprex package (v2.0.1)
And use it in a function.
sumAntiDiag <- function(M){
nr <- nrow(M)
nc <- ncol(M)
d <- outer(seq.int(nr), seq.int(nc), `+`)
tapply(M, d, sum)
}
n <- 3
x <- c(0.85, 0.1, 0.05)
M <- matrix(NA, n, n);
for(i in 1:n){
for(j in 1:n){
M[i,j] = x[i] * x[j]
}}
sumAntiDiag(M)
#> 2 3 4 5 6
#> 0.7225 0.1700 0.0950 0.0100 0.0025
Created on 2022-03-24 by the reprex package (v2.0.1)

You could do:
sapply(seq(3), function(x) seq(3) + x - 2)
#> [,1] [,2] [,3]
#> [1,] 0 1 2
#> [2,] 1 2 3
#> [3,] 2 3 4
Or more generally,
anti_diagonal <- function(n) sapply(seq(n), function(x) seq(n) + x - 2)
For example:
anti_diagonal(6)
#> [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,] 0 1 2 3 4 5
#> [2,] 1 2 3 4 5 6
#> [3,] 2 3 4 5 6 7
#> [4,] 3 4 5 6 7 8
#> [5,] 4 5 6 7 8 9
#> [6,] 5 6 7 8 9 10

Try the code below by defining a function f using embed from base R, i.e.,
f <- function(n) embed(seq(2 * n - 1) - 1, n)[, n:1]
such that
> f(3)
[,1] [,2] [,3]
[1,] 0 1 2
[2,] 1 2 3
[3,] 2 3 4
> f(4)
[,1] [,2] [,3] [,4]
[1,] 0 1 2 3
[2,] 1 2 3 4
[3,] 2 3 4 5
[4,] 3 4 5 6
> f(5)
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 2 3 4 5
[3,] 2 3 4 5 6
[4,] 3 4 5 6 7
[5,] 4 5 6 7 8

You can use sequence:
function(n) matrix(sequence(rep(n, n), seq(n) - 1), nrow = n)
output
f <- function(n) matrix(sequence(rep(n, n), seq(n) - 1), nrow = n)
f(3)
[,1] [,2] [,3]
[1,] 0 1 2
[2,] 1 2 3
[3,] 2 3 4
f(5)
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 2 3 4 5
[3,] 2 3 4 5 6
[4,] 3 4 5 6 7
[5,] 4 5 6 7 8

Using indexing instead of tapply will speed things up a bit. Or Rcpp:
sumdiags <- function(mat, minor = TRUE) {
m <- ncol(mat)
if (minor) {
n <- nrow(mat)
lens <- c(pmin(1:n, m), pmin((m - 1L):1, n))
c(mat[1], diff(cumsum(mat[sequence(lens, c(1:n, seq(2L*n, by = n, length.out = m - 1L)), n - 1L)])[cumsum(lens)]))
} else {
Recall(mat[,m:1])
}
}
# compare to tapply solution
sumdiags2 <- function(mat, minor = TRUE) {
if (minor) {
as.numeric(tapply(mat, row(mat) + col(mat), sum))
} else {
Recall(mat[,ncol(mat):1])
}
}
# or Rcpp
Rcpp::cppFunction('NumericVector sumdiagsRcpp(const NumericMatrix& mat) {
const int n = mat.nrow();
const int m = mat.ncol();
NumericVector x (n + m - 1);
for(int row = 0; row < n; row++) {
for(int col = 0; col < m; col++) {
x[row + col] += mat(row, col);
}
}
return x;
}')
# OP data
x <- c(0.85, 0.1, 0.05)
m <- outer(x, x)
sumdiags(m)
#> [1] 0.7225 0.1700 0.0950 0.0100 0.0025
sumdiags2(m)
#> [1] 0.7225 0.1700 0.0950 0.0100 0.0025
sumdiagsRcpp(m)
#> [1] 0.7225 0.1700 0.0950 0.0100 0.0025
# bigger matrix for benchmarking
m <- matrix(runif(1e6), 1e3)
microbenchmark::microbenchmark(sumdiags = sumdiags(m),
sumdiags2 = sumdiags2(m),
sumdiagsRcpp = sumdiagsRcpp(m),
check = "equal")
#> Unit: milliseconds
#> expr min lq mean median uq max neval
#> sumdiags 9.985302 10.266350 13.686723 10.803401 17.5274 22.387601 100
#> sumdiags2 55.790402 65.140051 78.763478 67.120051 70.4165 183.936801 100
#> sumdiagsRcpp 2.192201 2.378651 2.599326 2.631751 2.7050 4.038301 100

Related

Filling matrices with NA to meet desired dimensions

I have a list of matrices that I've created. The matrices in the list have different dimensions, and I would like to fill the matrices that don't have a 3x3 dimension with NAs.
I have included my expected outcome below. I would like to include this in a if statement, where if the matrix in the list doesn't have a 3x3 dimension I would like to added empty columns/rows to those matrices and fill them with an NA. Is there an efficient way of doing this in base r?
# Created Matrices
m1 <- matrix(1:9, 3,3)
m2 <- matrix(1:4, 2,2)
m3 <- matrix(1:3, 3, 1)
# Matrices into a list
l1 <- list(m1, m2, m3)
l1
# Expected Matrices and outputs
m2_new <- matrix(c(1,2,NA,3, 4, rep(NA, 4)), 3,3)
m3_new <- matrix(c(1,2,3,rep(NA, 6)), 3,3)
expected <- list(m1, m2_new, m3_new)
One option would be to create a NA matrix and replace the values with the 'x' based on the row/col index
dummy <- matrix(ncol = 3, nrow = 3)
l2 <- lapply(l1, function(x) replace(dummy, cbind(c(row(x)), c(col(x))), x))
-checking
> all.equal(l2, expected)
[1] TRUE
You can replace parts of a matrix with matrix indexing.
mat <- array(dim = c(3, 3))
lapply(l1, function(x) `[<-`(mat, 1:nrow(x), 1:ncol(x), x))
# [[1]]
# [,1] [,2] [,3]
# [1,] 1 4 7
# [2,] 2 5 8
# [3,] 3 6 9
#
# [[2]]
# [,1] [,2] [,3]
# [1,] 1 3 NA
# [2,] 2 4 NA
# [3,] NA NA NA
#
# [[3]]
# [,1] [,2] [,3]
# [1,] 1 NA NA
# [2,] 2 NA NA
# [3,] 3 NA NA
Update see comment by Darren Tsai:
n <- 3
l2 <- lapply(l1, function(x) rbind(x, matrix(ncol = ncol(x), nrow = n - nrow(x))))
x <- sapply(l2, `length<-`, max(lengths(l2)))
list(m1 = matrix(x[,1],3), m2 = matrix(x[,2],3), m3 = matrix(x[,3],3))
$m1
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
$m2
[,1] [,2] [,3]
[1,] 1 3 NA
[2,] 2 4 NA
[3,] NA NA NA
$m3
[,1] [,2] [,3]
[1,] 1 NA NA
[2,] 2 NA NA
[3,] 3 NA NA
First answer: not correct output:
Here is another approach:
x <- t(sapply(l1, `length<-`, max(lengths(l1))))
l2 <- list(x[,1:3], x[,4:6], x[,7:9])
l2
[[1]]
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 2 3
[3,] 1 2 3
[[2]]
[,1] [,2] [,3]
[1,] 2 2 2
[2,] 4 NA NA
[3,] NA NA NA
[[3]]
[,1] [,2] [,3]
[1,] 3 3 3
[2,] NA NA NA
[3,] NA NA NA
I think there are better solutions but mine will handle a 1x1 matrix as well, which is really just a vector.
You can use the function I've made here resize_matrix in your code however you'd like. It is pretty verbose, but I thinks it's easy to understand exactly what it's doing under the hood. Note: the function is meant to be used in an lapply() call.
The input:
m1 <- matrix(1:9, 3,3)
m2 <- matrix(1:4, 2,2)
m3 <- matrix(1:3, 3, 1)
m4 <- matrix(1:3, 1, 3)
m5 <- matrix(1, 1, 1)
# Matrices into a list
l1 <- list(m1, m2, m3, m4, m5)
l1
#> [[1]]
#> [,1] [,2] [,3]
#> [1,] 1 4 7
#> [2,] 2 5 8
#> [3,] 3 6 9
#>
#> [[2]]
#> [,1] [,2]
#> [1,] 1 3
#> [2,] 2 4
#>
#> [[3]]
#> [,1]
#> [1,] 1
#> [2,] 2
#> [3,] 3
#>
#> [[4]]
#> [,1] [,2] [,3]
#> [1,] 1 2 3
#>
#> [[5]]
#> [,1]
#> [1,] 1
The function:
resize_matrix <- function(mat, desired_rows = 3, desired_columns = 3){
needed_cols <- desired_columns - dim(mat)[2]; needed_cols
needed_rows <- desired_rows - dim(mat)[1]; needed_rows
if (dim(mat)[1] == 1 & dim(mat)[2] == 1){
# we're give a matrix with a single value, expand correctly
final_mat <- matrix(NA, nrow = desired_rows, ncol = desired_columns)
final_mat[1,1] <- mat
} else if (needed_cols > 0 & needed_rows > 0){
# we need to add both rows and columns
col_res <- rep(NA, needed_rows)
row_res <- rep(NA, needed_cols)
mat_temp1 <- rbind(mat, col_res)
final_mat <- unname(cbind(mat_temp1, row_res))
} else if (needed_cols > 0 & needed_rows == 0) {
# we need to add only columns
row_res <- matrix(rep(NA, needed_cols),
ncol = needed_cols, nrow = desired_rows)
final_mat <- unname(cbind(mat, row_res))
} else if (needed_cols == 0 & needed_rows > 0) {
# we need to add only rows
col_res <- matrix(rep(NA, needed_rows),
ncol = desired_columns, nrow = needed_rows)
final_mat <- unname(rbind(mat, col_res))
} else {
# we don't need to add anything, return the matrix
final_mat <- mat
}
return(final_mat)
}
The output:
lapply(l1, FUN = resize_matrix)
#> [[1]]
#> [,1] [,2] [,3]
#> [1,] 1 4 7
#> [2,] 2 5 8
#> [3,] 3 6 9
#>
#> [[2]]
#> [,1] [,2] [,3]
#> [1,] 1 3 NA
#> [2,] 2 4 NA
#> [3,] NA NA NA
#>
#> [[3]]
#> [,1] [,2] [,3]
#> [1,] 1 NA NA
#> [2,] 2 NA NA
#> [3,] 3 NA NA
#>
#> [[4]]
#> [,1] [,2] [,3]
#> [1,] 1 2 3
#> [2,] NA NA NA
#> [3,] NA NA NA
#>
#> [[5]]
#> [,1] [,2] [,3]
#> [1,] 1 NA NA
#> [2,] NA NA NA
#> [3,] NA NA NA
Created on 2022-04-16 by the reprex package (v2.0.1)

How can i create this exact 5x5 matrix using only matrix operations?

I was given this matrix and i have to create it using exclusively matrix operations.
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
So this is what i have done, but im not sure if this is actually considered as matrix operations in order to create my MATNew
mat1 <- matrix(c(0:4), nrow=1, ncol=5) ; print(mat1)
mat2 <- matrix(c(1:5), nrow=1, ncol=5) ; print(mat2)
mat3 <- matrix(c(2:6), nrow=1, ncol=5) ; print(mat3)
mat4 <- matrix(c(3:7), nrow=1, ncol=5) ; print(mat4)
mat5 <- matrix(c(4:8), nrow=1, ncol=5) ; print(mat5)
MATNew <- matrix(cbind(mat1,mat2,mat3,mat4,mat5), 5, 5) ; print(MATNew)
outer(0:4, 0:4, `+`)
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 1 2 3 4
# [2,] 1 2 3 4 5
# [3,] 2 3 4 5 6
# [4,] 3 4 5 6 7
# [5,] 4 5 6 7 8
You can use the row() and col() functions (if this is homework, make sure to cite your sources ...)
m <- matrix(NA, 5, 5)
row(m) + col(m)-2
A matrix-only solution
matrix( rep(0:8,5), 5 )[,1:9%%2==1]
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 2 3 4 5
[3,] 2 3 4 5 6
[4,] 3 4 5 6 7
[5,] 4 5 6 7 8
Try this?
> embed(0:8, 5)[, 5:1]
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 2 3 4 5
[3,] 2 3 4 5 6
[4,] 3 4 5 6 7
[5,] 4 5 6 7 8
Benchmark for kicks:
microbenchmark::microbenchmark(
outer = outer(0:4, 0:4, `+`),
rowcol = {m <- matrix(NA, 5, 5); row(m) + col(m)-2},
matrix1 = matrix(rep(0:8,5), 5)[,1:9%%2==1],
matrix2 = matrix(rep(0:8,5), 5)[,c(TRUE, FALSE)],
matrix3 = matrix(rep(0:4, each = 5) + 0:4, 5),
sequence = matrix(sequence(rep(5, 5), 0:4), 5), times = 1e5)
#> Unit: microseconds
#> expr min lq mean median uq max neval
#> outer 5.100 6.200 8.706730 6.501 7.202 6628.401 1e+05
#> rowcol 2.900 3.801 5.097179 4.002 4.402 4985.501 1e+05
#> matrix1 2.900 3.701 5.159770 4.000 4.400 3621.901 1e+05
#> matrix2 2.400 3.002 4.063637 3.201 3.601 2395.001 1e+05
#> matrix3 2.000 2.600 3.535451 2.701 3.001 2517.101 1e+05
#> sequence 3.701 4.601 6.179183 4.901 5.401 3303.102 1e+05

How can I create this exact 5x5 matrix using only for loops?

0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
So I was given this matrix and was told to create it using only for loops. What i have done so far is using cbind(0:4,1+(0:4),2+(0:4),3+(0:4),4+(0:4)) but i cant figure out a way to do so with the for function.
You were on the right track. If you rewrite your current
cbind(0:4,1+(0:4),2+(0:4),3+(0:4),4+(0:4))
as
cbind(0+(0:4),1+(0:4),2+(0:4),3+(0:4),4+(0:4))
you might notice that the thing that you are adding to 0:4 is implicitly a loop index.
Make it explicit:
m = c()
for(i in 0:4){
m = cbind(m,i+(0:4))
}
print(m)
Output:
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 2 3 4 5
[3,] 2 3 4 5 6
[4,] 3 4 5 6 7
[5,] 4 5 6 7 8
One way:
mat <- matrix(0L, nrow=5, ncol=5)
for (i in 0:4) {
for (j in 0:4) {
mat[i + 1, j + 1] <- i + j
}
}
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 1 2 3 4
# [2,] 1 2 3 4 5
# [3,] 2 3 4 5 6
# [4,] 3 4 5 6 7
# [5,] 4 5 6 7 8
And technically *apply functions are loops as well:
sapply(0:4, \(x) 0:4 + x)
You can just create an empty matrix first and then fill it with two for-loops iterating over rows and columns. Playing a little bit around with the variable to write into the matrix (count) I figured out that this is a suitable solution.
matrix2fill <- matrix(NA, 5,5)
count = 0
for (i in 1:5){
for (j in 1:5){
matrix2fill[j,i] = count
count = count + 1
}
count = i
}
matrix2fill
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 2 3 4 5
[3,] 2 3 4 5 6
[4,] 3 4 5 6 7
[5,] 4 5 6 7 8
Yet another way:
mymat <- matrix(NA, nrow = 5, ncol = 5)
i_mat <- 1
for (i in 0:4) {
mymat[seq(i_mat, i_mat + 4)] <- seq(i, i + 4)
i_mat <- i_mat + 5
}
mymat
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 3 4
[2,] 1 2 3 4 5
[3,] 2 3 4 5 6
[4,] 3 4 5 6 7
[5,] 4 5 6 7 8
Try this:
a = matrix(1:25, nrow=5, ncol=5)
for (i in 1:5) {
for (j in 1:5) {
a[i][j] = (i-1) + (j-1)
}
}
print(a)

Combine matrices row by row

I have two matrices of arbitrary sizes, e.g. matrix 1 (n * m) and matrix 2 (k * l). Is there a (convenient) way in R to cbind them row-by-row, to form a (n * k) * (m + l) matrix where each row of matrix 1 has a chance to be cbinded to each row of matrix 2? It is a complete row-by-row combination so the order does not matter.
For example, is there a function f such that:
please click to view
Thanks!
In the future, please include example data that is copy/pasteable, not just a picture.
m1 = matrix(1:6, ncol = 2)
m2 = matrix(7:12, ncol = 3)
combos = expand.grid(1:nrow(m1), 1:nrow(m2))
cbind(m1[combos$Var1, ], m2[combos$Var2, ])
# [,1] [,2] [,3] [,4] [,5]
# [1,] 1 4 7 9 11
# [2,] 2 5 7 9 11
# [3,] 3 6 7 9 11
# [4,] 1 4 8 10 12
# [5,] 2 5 8 10 12
# [6,] 3 6 8 10 12
There might be a better solution, but this works for smaller size problems:
A <- matrix(c(1,2,3,2,3,4), nrow = 3)
B <- matrix(c(5,6,6,7,7,8), nrow = 2)
temp <- lapply(1:nrow(A), function(x){
C = apply(B, 1, function(y){
c(A[x,],y)
})
return(t(C))
})
output <- do.call(rbind, temp)
cbind(A[rep(1:(x<-nrow(A)),each=y<-nrow(B)),],B[rep(1:y,x),])
[,1] [,2] [,3] [,4] [,5]
[1,] 1 2 5 6 7
[2,] 1 2 6 7 8
[3,] 2 3 5 6 7
[4,] 2 3 6 7 8
[5,] 3 4 5 6 7
[6,] 3 4 6 7 8
Break down:
x=nrow(A)
y=nrow(B)
cbind(A[rep(1:x,each=y),],B[rep(1:y,x),])

Minimum of cells in two matrices within a moving kernel

I have two matrices m1 and m2.
m1 <- matrix(1:16, ncol = 4)
m2 <- matrix(16:1, ncol = 4)
# > m1
# [,1] [,2] [,3] [,4]
# [1,] 1 5 9 13
# [2,] 2 6 10 14
# [3,] 3 7 11 15
# [4,] 4 8 12 16
# > m2
# [,1] [,2] [,3] [,4]
# [1,] 16 12 8 4
# [2,] 15 11 7 3
# [3,] 14 10 6 2
# [4,] 13 9 5 1
I want to find the minimum between the two matrices for each cell within a moving kernel of 3x3. The outer margines should be ignored, i.e. they can be filled with NAs and the min function should then have na.rm = TRUE. The result should look like this:
# > m3
# [,1] [,2] [,3] [,4]
# [1,] 1 1 3 3
# [2,] 1 1 2 2
# [3,] 2 2 1 1
# [4,] 3 3 1 1
I have already tried a combination of pmin{base} and runmin{caTools} like this:
pmin(runmin(m1, 3, endrule = "keep"),
runmin(m2, 3, endrule = "keep"))
However, this did not work. Probably due to the fact that
"If x is a matrix than each column will be processed separately."
(from ?runmin)
Is there any package, that performs such operations, or is it possible to apply?
Here is a base R approach:
m = pmin(m1, m2)
grid = expand.grid(seq(nrow(m)), seq(ncol(m)))
x = apply(grid, 1, function(u) {
min(m[max(1,u[1]-1):min(nrow(m), u[1]+1), max(1,u[2]-1):min(ncol(m), u[2]+1)])
})
dim(x) = dim(m)
#> x
# [,1] [,2] [,3] [,4]
#[1,] 1 1 3 3
#[2,] 1 1 2 2
#[3,] 2 2 1 1
#[4,] 3 3 1 1

Resources