Here is another option with base R :
df first split into Name , and then, among each subset, for each Sale , it looks to see if there is an email (webinar) from the Sale for 21 days. Finally, the list is not split according to Name .
You just need to replace FALSE with no and TRUE with yes .
df_split <- split(df, df$Name) df_split <- lapply(df_split, function(tab){ i_s <- which(tab[,2]=="Sale") tab$Email21[i_s] <- sapply(tab[i_s, 3], function(d_s){any(tab[tab$ActivityType=="Email", 3] >= d_s-21)}) tab$Webinar21[i_s] <- sapply(tab[i_s, 3], function(d_s){any(tab[tab$ActivityType=="Webinar", 3] >= d_s-21)}) tab }) df_res <- unsplit(df_split, df$Name) df_res
<strong> data
df <- structure(list(Name = c("John", "John", "John", "John", "John", "John", "Tom", "Tom", "Tom", "Tom", "Tom", "Tom"), ActivityType = c("Email", "Webinar", "Sale", "Webinar", "Sale", "Sale", "Email", "Webinar", "Sale", "Webinar", "Sale", "Sale"), ActivityDate = structure(c(16071, 16075, 16090, 16154, 16161, 16252, 16436, 16440, 16455, 16519, 16526, 16617), class = "Date")), .Names = c("Name", "ActivityType", "ActivityDate"), row.names = c(NA, -12L), index = structure(integer(0), ActivityType = c(1L, 7L, 3L, 5L, 6L, 9L, 11L, 12L, 2L, 4L, 8L, 10L)), class = "data.frame")