Q:

Updating fields in a data.frame according to linear intervals

I have this example data.frame:
my.df = data.frame(id = rep("a",10), start = seq(100, 1000, 100), end = seq(150, 1050, 100), flag = c(1,0,0,1,0,1,0,1,0,1))
> my.df
   id start  end flag
1   a   100  150    1
2   b   200  250    0
3   c   300  350    0
4   d   400  450    1
5   e   500  550    0
6   f   600  650    1
7   g   700  750    0
8   h   800  850    1
9   i   900  950    0
10  j  1000 1050    1

The rows are linear intervals, sorted by my.df$start and then my.df$end in ascending order . Note that any two rows with my.df$flag = 1 are by definition flanked by at least one row with my.df$flag = 0.
What I want is to update the my.df$id field in the following way:
First, update the rows with my.df$flag = 1 such that any appearance of a row with my.df$flag = 1 will increment the updated my.df$id field for that row from a.i to a.i+1, where i is an integer that will be initialized at 0. 
Then, all the rows with my.df$flag = 0 need to be updated in the following way:
The rows from the beginning of my.df until the first row with my.df$flag = 1 will have my.df$id = a.1. Any rows intervening any two rows with my.df$flag = 1 (i.e. for any two rows with my.df$flag = 1 a.i and a.i+1) the rows for which my.df$start is smaller than the mid point between the end of my.df$id = a.i and start of my.df$id = a.i+1 of the two rows with my.df$flag = 1 will be updated with my.df$id = a.i and the rows for which my.df$start is greater than or equal to the that same midpoint will be updated with my.df$id = a.i+1. The rows with my.df$flag = 0 from after the last row with my.df$flag = 1 until the last row of my.df will have that my.df$id value of the last row with my.df$flag = 1. Note that it is possible that the first and/or last rows of my.df will actually be rows with my.df$flag = 1.
So, the output for this example should be:
> my.updated.df
    id start  end flag
1  a.1   100  150    1
2  a.1   200  250    0
3  a.2   300  350    0
4  a.2   400  450    1
5  a.2   500  550    0
6  a.3   600  650    1
7  a.3   700  750    0
8  a.4   800  850    1
9  a.4   900  950    0
10 a.5  1000 1050    1

A:

You could use cumsum on d$flag to split your data into "chunks". For each of these you would then compute the midpoint between "end" and "start", and use it in order to define d$id by comparing it to d$start. I have slightly modified your example data frame in order to include the edge cases where rows with flag = 0 appear before the first flag=1 and after the last flag=1. Note that if d$id was a factor like in your example my.df we would first need to convert it into a character for this to work (d$id <- as.character(d$id)).
d <- structure(list(id = c("a", "a", "a", "a", "a", "a", "a", "a", 
    "a", "a", "a", "a"), start = c(100, 100, 200, 300, 400, 500, 
    600, 700, 800, 900, 1000, 1000), end = c(110, 150, 250, 350, 
    450, 550, 650, 750, 850, 950, 1050, 1100), flag = c(0, 1, 0, 
    0, 1, 0, 1, 0, 1, 0, 1, 0)), .Names = c("id", "start", "end", 
    "flag"), row.names = c(NA, 12L), class = "data.frame")

# Create a 'subset index'. Rows with the same index will be
# compared to the same midpoint.
d$subset.idx <- cumsum(d$flag) 

# For each index, compute the midpoint that the 'start'
# value of each row with that index needs to be compared to
mid <- d[d$flag == 1, ]
mid$midpoint <- c((mid$start[2:nrow(mid)] - mid$end[1:(nrow(mid) - 1)]) / 2 +
    mid$end[1:(nrow(mid) - 1)], 0)
mid <- mid[c("subset.idx", "midpoint")]
mid <- rbind(c(0, 0), mid) # before merging with d, add a row for subset.idx = 0 

# Merge with d and assign id by comparing start to midpoint
d <- merge(d, mid)
d$id <- ifelse(d$start < d$midpoint,
    paste0("a.", d$subset.idx),
    paste0("a.", d$subset.idx + 1))
# Finally, handle edge cases, i.e. those with flag 0 before and after
# the first and last flag = 1 respectively
d[d$subset.idx == 0, "id"] <- "a.1"
d[d$subset.idx == max(d$subset.idx), "id"] <- paste0("a.", max(d$subset.idx))
d <- d[- which(names(d) %in% c("subset.idx", "midpoint"))]

