Stata_user
Stata_user

Reputation: 564

Create matrix with for-loop

I am trying to create the following matrix A for n rows and n+1 columns. n will likely be around 20 or 30, but for the purpose of the question I put it at 4 and 5.

enter image description here

Here is what I have so far:

N <- 5             # n+1
n <- 4             # n
columns <- list()

# first column:
columns[1] <- c(-1, 1, rep(0, N-2))

# all other columns:
for(i in N:2) { 
 columns[i] <- c((rep(0, N-i), 1, -2, 1, rep(0, i-3)))
}

# combine into matrix:

A <- cbind(columns)

I keep getting the following error msg:

In columns[1] <- c(-1, 1, rep(0, N - 2)) :
  number of items to replace is not a multiple of replacement length

And later

"for(i in N:2) { 
  columns[i] <- c((rep(0, N-i),"
 }
Error: unexpected '}' in "}"

Upvotes: 0

Views: 85

Answers (3)

IceCreamToucan
IceCreamToucan

Reputation: 28675

You could use data.table::shift to shift the vector c(1, -2, 1, 0) by all increments from -1 (backwards shift / lead by 1) to n - 1 (forward shift / lagged by n - 1) and then cbind all the shifted outputs together. The first-row first-column element doesn't follow this pattern so that's fixed at the end.

library(data.table)

out <- do.call(cbind, shift(c(1, -2, 1, 0), seq(-1, n - 1), fill = 0))
out[1, 1] <- -1

out
#      [,1] [,2] [,3] [,4] [,5]
# [1,]   -1    1    0    0    0
# [2,]    1   -2    1    0    0
# [3,]    0    1   -2    1    0
# [4,]    0    0    1   -2    1

Upvotes: 0

ThomasIsCoding
ThomasIsCoding

Reputation: 101064

  • I guess you can try the for loop below to create your matrix A:
N <- 5
n <- 4
A <- matrix(0,n,N)
for (i in 1:nrow(A)) {
  if (i == 1) {
    A[i,1:2] <- c(-1,1)
  } else {
    A[i,i+(-1:1)] <- c(1,-2,1)
  }
}

such that

> A
     [,1] [,2] [,3] [,4] [,5]
[1,]   -1    1    0    0    0
[2,]    1   -2    1    0    0
[3,]    0    1   -2    1    0
[4,]    0    0    1   -2    1
  • Another solution is to use outer, and this method would be faster and looks more compact than the for loop approach, i.e.,
A <- `diag<-`(replace(z<-abs(outer(1:n,1:N,"-")),!z %in% c(0,1),0),
              c(-1,rep(-2,length(diag(z))-1)))

Upvotes: 2

Gregor Thomas
Gregor Thomas

Reputation: 145755

I thought this would be fast compared to the loop, but when I tested on a 5000x5001 example, the loop in ThomasIsCoding's answer was about 5x faster. Go with that one!

N = 5
n = N - 1

A = matrix(0, nrow = n, ncol = N)

delta = row(A) - col(A)

diag(A) = -2
A[delta %in% c(1, -1)] = 1
A[1, 1] = -1
A
#      [,1] [,2] [,3] [,4] [,5]
# [1,]   -1    1    0    0    0
# [2,]    1   -2    1    0    0
# [3,]    0    1   -2    1    0
# [4,]    0    0    1   -2    1

Upvotes: 2

Related Questions