tags:

views:

793

answers:

3

How do you properly write a double for loop in R?

For example, in C I would do

int i, j;
for (i = 1; i < 6; i++) {
    for (j=i; j <= 3; j++) {
        printf("%i,%i\n",i,j);
    }
    // Do more operations for i > 3...
}

which would generate the (artificial) sequence:

1,1
1,2
1,3
2,2
2,3
3,3

In R you do not get the same behaviour when you write

for (i in 1:6) {
    for (j in i:3) {
        print(paste(i,j,sep=","))
    }
}

so I've been reduced to doing something like

for (i in 1:6) {
    j <- i
    while (j <= 3) {
        print(paste(i,j,sep=","))
        j <- j+1
    }
}

Is there a better way?

As Shane mentioned, maybe I should make this clear: I am particularly interested in the code-style matching the mathematics to make it easier for students to understand. It seems that students are the most comfortable with "for" loops.

In particular, I want my students to simulate a LIBOR market model. The dynamics of the forward rate are to be simulated under the same probability measure. As such, for each time step and each forward rate, the appropriate drift correction \mu_i needs to be calculated and added.

+4  A: 

You can of course nest loops:

R> for (i in 1:3) for (j in 1:3) cat(i,j,i*j, "\n")
1 1 1 
1 2 2 
1 3 3 
2 1 2 
2 2 4 
2 3 6 
3 1 3 
3 2 6 
3 3 9 
R>

There is general sense that you should not as vectorised calls can be easier to read and write:

R> outer(1:3,1:3, "*")
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    2    4    6
[3,]    3    6    9
R>

but if it is easier to develop with nexted loops, then do so.

As for the 'second index dependent on first index' issue you can use lower.tri(), upper.tri() or indexing to achieve that too.

R> X <- expand.grid(x=1:3, y=1:3)
R> X <- X[ X$x >= X$y, ]
R> outer(X$x, X$y, "*")
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    1    1    2    2    3
[2,]    2    2    2    4    4    6
[3,]    3    3    3    6    6    9
[4,]    2    2    2    4    4    6
[5,]    3    3    3    6    6    9
[6,]    3    3    3    6    6    9
R>
Dirk Eddelbuettel
Thanks, using outer is probably a good way to do it, I will give it a go. Though it makes the algorithm no longer looks like the math, so it makes it hard for my students to understand.
Proceris
+3  A: 

Sorry for asking what is potentially a dumb question, but in what sense can R not do this? This works perfectly fine for me:

N <- 5
for (i in 0:(2*N)) {
   for (j in i:N) {
        print(paste(i,j,sep=","))
    }
}

Could it be that you just didn't put parentheses around the end of your sequence?

Edit: I see...you want to enforce that the sequence i:3 is always <= 3? I don't think that's possible with either a sequence or within the clause of a for statement. You can set a break within the loop, but that's no better than your current approach:

for (i in 1:6) {
    for (j in i:3) {
        if(j > 3) break()
        print(paste(i,j,sep=","))
    }
}

Here's another way to generate this sequence without a for loop:

x <- cbind(rep(c(1,2,3), 3),
    rep(c(1,2,3), each=3))

Or with expand.grid (as per Dirk's suggestion):

x <- expand.grid(x=1:3, y=1:3)

Then remove the unwanted cases:

x[x[,1] >= x[,2],]

Edit 2: This may not suit your needs, but I believe that Quantlib has a Libor Market Model implementation. I'm not sure if it's exposed in RQuantlib.

Shane
I've edited my question to hopefully make it clearer now. Thanks.
Proceris
Using a "break" is nice, it means I don't need to explain "while". Thanks.
Proceris
+4  A: 

The issue here is that i:3 is meaningful when i>3. For example, 5:3 gives (5,4,3). All you need is a simple if statement to prevent the second loop being run when i>3.

for (i in 1:6) {
    if(i < 4) {
        for (j in i:3) {
            print(paste(i,j,sep=","))
        }
    }
    # Do more operations for i > 3...
}

However, if possible try to avoid the explicit looping. Dirk's and Shane's answers provide some ideas on how to do this.

Rob Hyndman
This is nice, it means I don't need to explain what "while" does.
Proceris