Imperative code in R keeping track of state A friend of mine has a spreadsheet where she coded a classroom video for various teaching events, which I needed to help her plot. For each recording, she used several video cassettes, and her time codes were relative to each video cassette (ie. they always start on 0). To be able to plot these on a timeline, I needed to convert the times into a relative (continuous) time. The code below works perfectly (and I've solved the problem), however it doesn't look good to me. It looks like the code I used to write in Ruby, before I began playing with R, Haskell etc. 

This is not the first time I've come across this kind of a problem - iterating through rows, or lines of text, needing to keep track of several different kinds of "state", etc. I would be curious if this code could be rewritten to look cleaner, more idiomatic. 

Example dataset




structure(list(code = structure(c(4L, 3L, 1L, 1L, 5L, 4L, 3L, 
1L, 5L, 2L), .Label = c("CC-NoZZ", "CC-ZZ", "FBP", "KK-NoZZ", 
"KK-ZZ"), class = "factor"), src = structure(c(1L, 1L, 1L, 2L, 
2L, 3L, 3L, 4L, 4L, 4L), .Label = c("alfred1.mov", "alfred2.mov", 
"claire1.mov", "claire2.mov"), class = "factor"), class = structure(c(1L, 
1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L), .Label = c("alfred's class", 
"claire's class"), class = "factor"), start = c(0L, 2L, 6L, 0L, 
10L, 1L, 5L, 0L, 6L, 14L), end = c(2L, 4L, 8L, 4L, 12L, 3L, 9L, 
4L, 10L, 20L)), .Names = c("code", "src", "class", "start", "end"
), class = "data.frame", row.names = c(NA, -10L))



Example code



src = ""
class = ""
delta = 0
db$corr.start = NA
db$corr.end = NA
db$delta = NA

for(i in 1:nrow(db)) {
    row &lt;- db[i,]
    if(row$src != src) {
      delta = db[i-1,]$end + delta
      src = row$src
    }

    if(row$class != class) {
      delta = 0
      class = row$class
    }

    db[i,]$corr.start = db[i,]$start + delta
    db[i,]$corr.end = db[i,]$end + delta

    db[i,]$delta = delta
}


Example output




      code         src          class start end corr.start corr.end delta
1  KK-NoZZ alfred1.mov alfred's class     0   2          0        2     0
2      FBP alfred1.mov alfred's class     2   4          2        4     0
3  CC-NoZZ alfred1.mov alfred's class     6   8          6        8     0
4  CC-NoZZ alfred2.mov alfred's class     0   4          8       12     8
5    KK-ZZ alfred2.mov alfred's class    10  12         18       20     8
6  KK-NoZZ claire1.mov claire's class     1   3          1        3     0
7      FBP claire1.mov claire's class     5   9          5        9     0
8  CC-NoZZ claire2.mov claire's class     0   4          9       13     9
9    KK-ZZ claire2.mov claire's class     6  10         15       19     9
10   CC-ZZ claire2.mov claire's class    14  20         23       29     9
R encourages vector-based operations instead of loops. But in this case, I don't think there's a good way around tracking the state of delta. 

This might be a slightly better way to create a vector delta:

delta &lt;- 0
vdelta &lt;- c(0)
for (i in 2:nrow(db)) {
  row &lt;- db[i,]
  prev &lt;- db[i-1,]
  if (row$class != prev$class) {
    delta &lt;- 0
  } else if (row$src != prev$src) {
    delta &lt;- prev$end + delta
  }
  vdelta &lt;- c(vdelta, delta)
}


Based on vdelta you can easily create the columns you need:

db$corr.start &lt;- db$start + vdelta
db$corr.end &lt;- db$end + vdelta
db$delta &lt;- vdelta