I would use tapply
. Given your data:
tab1 <- data.frame(
pat = c(rep("P1", 3), rep("P2", 3)),
t = c(0, 5, 10, 0, 5, 10),
conc = c(788, 720, 655, 644, 589, 544))
this one-liner will do it for you in the way you are hinting at in your post:
> tab1$conc / tab1$conc[tab1$t == 0][tapply(tab1$pat, tab1$pat)]
[1] 1.0000000 0.9137056 0.8312183 1.0000000 0.9145963 0.8447205
The tapply
without any function creates an row index matching patient id (number) for each row. I find this method rather fast and useful. But that assumes your patient ids' are ordered. If that is an issue, we can make sure they fit the patient id order:
> tab1$conc / tab1$conc[tab1$t == 0][order(unique(tab1$pat))][tapply(tab1$pat, tab1$pat)]
[1] 1.0000000 0.9137056 0.8312183 1.0000000 0.9145963 0.8447205
If you are using this often I would write a function for it, i.e. like this:
myFract <- function(obj, x = "conc", id = "pat", time = "t", start = NULL) {
if (is.null(start)) start <- min(obj[, time])
ii <- which(obj[, time] == start)
ii <- ii[order(unique(obj[, id]))][tapply(obj[, id], obj[, id])]
obj[, x] / obj[ii, x]
}
Such that:
> myFract(tab1)
[1] 1.0000000 0.9137056 0.8312183 1.0000000 0.9145963 0.8447205