shrgm
shrgm

Reputation: 1334

R - Number of days since last occurrence

I have a data frame that looks like this:

 id     date
 1001   2012-10-11
 1005   2013-02-20
 1005   2012-11-21
 1005   2014-03-14
 1003   2013-10-25
 1003   2013-11-30

I need to find, for each row, the number of days that have passed since the last occurrence of that id. For the above example, the answer would look like this:

 id     date        no_of_days
 1001   2012-10-11  NA
 1005   2013-02-20  91
 1005   2012-11-21  NA
 1005   2014-03-14  387
 1003   2013-10-25  NA
 1003   2013-11-30  36

A bit of searching got me to the point where I can add a new column with values that were generated by applying a function on subgroups (the R equivalent of STATA's "bysort"):

df$no_of_days<-with(df,ave(id,id,FUN=days_passed,na.rm=TRUE))

However, defining the new function days_passed is proving to be tricky as I have to find the last occurrence of that uniqid, and then formulate the function accordingly.

I'm new to R, so any help on this would be greatly appreciated. Thanks!

Upvotes: 1

Views: 905

Answers (2)

David Arenburg
David Arenburg

Reputation: 92282

Using data.table package you could try the following (though it doesn't preserve the order). Assuming df is your data set

library(data.table)
setkey(setDT(df)[, date := as.Date(date)], id, date) # If `date` is already of `Date` class you can skip the `as.Date` part
df[, no_of_days := c(NA, diff(date)) , by = id][]
#      id       date no_of_days
# 1: 1001 2012-10-11         NA
# 2: 1003 2013-10-25         NA
# 3: 1003 2013-11-30         36
# 4: 1005 2012-11-21         NA
# 5: 1005 2013-02-20         91
# 6: 1005 2014-03-14        387

Or (as @Arun suggesting) you can preserve the order by using order instead of setkey

setDT(df)[, date := as.Date(date)][order(id, date), 
            no := c(NA, diff(date)), by = id][]

Could as well try dplyr

library(dplyr)
df %>%
  mutate(date = as.Date(date)) %>%
  arrange(id, date) %>%
  group_by(id) %>%
  mutate(no_of_days = c(NA, diff(date)))

Upvotes: 4

akrun
akrun

Reputation: 886948

Or using ave (similar to @David Arenburg's approach)

 indx <- with(df, order(id, date))
 df1 <- transform(df[indx,], no_of_days=ave(as.numeric(date), id,
                    FUN= function(x) c(NA, diff(x))))[order(indx),]
 df1     
 #    id       date no_of_days
 #1 1001 2012-10-11         NA
 #2 1005 2013-02-20         91
 #3 1005 2012-11-21         NA
 #4 1005 2014-03-14        387
 #5 1003 2013-10-25         NA
 #6 1003 2013-11-30         36

data

df <- structure(list(id = c(1001L, 1005L, 1005L, 1005L, 1003L, 1003L
), date = structure(c(15624, 15756, 15665, 16143, 16003, 16039
), class = "Date")), .Names = c("id", "date"), row.names = c(NA, 
-6L), class = "data.frame")

Upvotes: 2

Related Questions