Search code examples
rloopsfor-loopnested-loops

R nested loop returning only the last iteration


I am trying to replicate 81-place cryoboxes used in lab storage system using a nested for loop. The following code illustrates the problem using 3-place boxes:

urine_random_df <- as.data.frame(c(seq(from = 10, to = 12, by = 1)))
boxcells <- vector()
cell_placeholder <- as.data.frame(c(seq(from = 1, to = 3, by = 1)))
for (i in 1: 3){
        #boxcells <- paste0("NEW", sprintf("%04d", as.numeric(urine_random_df[i,])))
        for (j in 1: nrow(cell_placeholder)){
                boxcells <- c(boxcells, paste(paste0("NEW", sprintf("%04d", as.numeric(urine_random_df[i,]))), cell_placeholder[j,], sep = "-"))        
        }

}


boxcells <- data.frame(boxcells)
names(boxcells) <- "box cells"
boxcells

The result of above is:

box cells
1 NEW0010-1
2 NEW0010-2
3 NEW0010-3
4 NEW0011-1
5 NEW0011-2
6 NEW0011-3
7 NEW0012-1
8 NEW0012-2
9 NEW0012-3

However, I would like to group the cells under their respective boxes like so:

   box cells
1  NEW0010
2  NEW0010-1
3  NEW0010-2
3  NEW0010-3
4  NEW0011
5  NEW0011-1
6  NEW0011-2
7  NEW0011-3
8  NEW0012
9  NEW0012-1
10 NEW0012-2
11 NEW0012-3

I tried to achieve this by adding boxcells <- paste0("NEW", sprintf("%04d", as.numeric(urine_random_df[i,]))) in the outer loop. When I re-ran the code with this piece, I get only the last box like so:

  box cells
1   NEW0012
2 NEW0012-1
3 NEW0012-2
4 NEW0012-3

It appears each iteration of the loop erases the last such that upon completion of the entire loop, only the last box remains. I found an existing thread here which suggests moving the "initialisation statements" outside the loop. However, in this case, the initialisation statements urine_random_df..., boxcells... and cell_placeholder... are already outside the loop. Thoughts?


Solution

  • I can think of very rare situations when you would do a nested for loop in R, even single for loop is very rare.

    I would solve this by doing something like

    temp <- expand.grid(sprintf("%04d", as.numeric(urine_random_df[,1])), c("", paste0("-",cell_placeholder[, 1])))
    boxcells <- data.frame(box_cells = paste0("NEW", paste0(temp[, 1], temp[, 2])))
    

    Which will return

       box_cells
    1    NEW0010
    2    NEW0011
    3    NEW0012
    4  NEW0010-1
    5  NEW0011-1
    6  NEW0012-1
    7  NEW0010-2
    8  NEW0011-2
    9  NEW0012-2
    10 NEW0010-3
    11 NEW0011-3
    12 NEW0012-3
    

    If you don't like the order, you could reorder by

    boxcells <- data.frame(box_cells = boxcells[order(as.numeric(substr(boxcells$box_cells, 6,7))), ])
    
       box_cells
    1    NEW0010
    2  NEW0010-1
    3  NEW0010-2
    4  NEW0010-3
    5    NEW0011
    6  NEW0011-1
    7  NEW0011-2
    8  NEW0011-3
    9    NEW0012
    10 NEW0012-1
    11 NEW0012-2
    12 NEW0012-3