TheEditingify
TheEditingify

Reputation: 25

Double/nested for loop in R

I've encountered a particular problem: What I want to do is to get values in my matrix basing on other values in the matrix, e.g

     [,1]     [,2]     [,3]
[1,]  110   0.0000   0.0000
[2,]    0 300.3475   0.0000
[3,]    0   0.0000 820.0785

And now, I want to have in first column down to last row values of:

[1,1] = 110
[2,1] = [1,1] * const
[3,1] = [2,1] * const

In second column:

[2,1] = 0
[2,2] = 300.3475
[3,2] = [2,2] * const

And the same thing for every column. So the point is to get value from [i, i], multiply it by a const and put into [i+1, i], then this value multiply by a const and put into [i+2, i]. And the same procedure for each column. So the matrix shown above will like this (const = 0.36624):

     [,1]     [,2]     [,3]
[1,] 110.00000   0.0000   0.0000
[2,]  40.28666 300.3475   0.0000
[3,]  14.75468 110.0000 820.0785

I've come up with something like this:

N <- 2
M.dim <- N+1
d <- 0.36624 #const
S0 <- c(110, 320, 820.075) #but length of this vector equals M.dim it's just example)
bin.mat <- matrix(rep(0), M.dim, M.dim)

for(i in 1:M.dim){
  for(j in i:N){
    bin.mat[i,i] <- S0[i]
    bin.mat[j + 1, i] <- bin.mat[j, i] * d
  }
}

And now, it is working - but it shows an error:

Error in `[<-`(`*tmp*`, j + 1, i, value = bin.mat[j, i] * d) : 
  subscript out of bounds

And being honest, I have no idea why. Probably beacuse I don't really get how this loop is working, I just tried maaaaaany variations of it and this is the only one actually doing what I want :D And while other calculations based on this matrix are incorrect, I suspect that as long as it works for low numbers of columns or rows it may not work properly for big ones.

Upvotes: 0

Views: 166

Answers (2)

niko
niko

Reputation: 5281

The problem essentially lies in the fact that 3:2 is a vector c(3,2). So when i = 3 you get out of bounds when trying bin.mat[j+1,i]. Here is a fix (keeping a loop)

for(i in 1:M.dim){
  bin.mat[i,i] <- S0[i]
  if (i >= 3) next # or i > N works as well
  for(j in i:N){
    bin.mat[j + 1, i] <- bin.mat[j, i] * d
  }
}
bin.mat
# returns
          [,1]     [,2]    [,3]
[1,] 110.00000   0.0000   0.000
[2,]  40.28640 320.0000   0.000
[3,]  14.75449 117.1968 820.075

Upvotes: 0

markus
markus

Reputation: 26343

Another approach using apply and cumprod

const <- 0.36624
mat[lower.tri(mat)] <- const
mat[upper.tri(mat)] <- 1

out <- apply(mat, 2, cumprod)
out[upper.tri(out)] <- 0
out
#          [,1]     [,2]     [,3]
#[1,] 110.00000   0.0000   0.0000
#[2,]  40.28640 300.3475   0.0000
#[3,]  14.75449 109.9993 820.0785

The idea is to replace the 0s in the lower triangle with const, the values in the upper triangle with 1 and then apply cumprod on each column.

data

mat <- structure(c(110, 0, 0, 0, 300.3475, 0, 0, 0, 820.0785), .Dim = c(3L, 
3L), .Dimnames = list(NULL, NULL))

Upvotes: 1

Related Questions