计算活动类型出现在过去21天窗口中的次数

时间:2015-12-14 18:47:34

标签: r data.table

这就是我的数据框架的样子。最右边的两列是我想要的列。

dt <- read.table(text='

    Name      ActivityType     ActivityDate  EmailLast21(Desired)     WebinarLast21(Desired)             
    John       Email            1/1/2014           1                  0  
    John       Webinar          1/5/2014           1                  1
    John       Webinar          1/20/2014          1                  2
    John       Webinar          3/25/2014          0                  1
    John       Email            4/1/2014           1                  1
    John       Email            4/20/2014          2                  0
    Tom        Email            1/1/2014           1                  0  
    Tom       Webinar           1/5/2014           1                  1
    Tom       Webinar           1/20/2014          1                  2
    Tom       Webinar           3/25/2014          0                  1
    Tom       Email             4/1/2014           1                  1
    Tom       Email             4/20/2014          2                  0

    ', header=T, row.names = NULL)

基于此前的解决方案:Events in last 21 days for every row by Name我正在尝试以下

require(data.table)
    setDT(dt)
    dt[, ActivityDate := as.Date(ActivityDate, format="%m/%d/%Y")]
    setkey(dt, Name, ActivityDate)

    roll_index <- function(x, types, roll=21) {
      lapply(types, function(type) {
        idx = x[ActivityType == type][x, roll=roll, which=TRUE]
        as.numeric(idx)
      })
    }
    dt[, c("Email_21", "Webinar_21") := roll_index(dt, c("Email", "Webinar"))]

但输出不是我想要的,因为我使用的是.numeric函数,而不是计算在过去21中出现网络研讨会电子邮件的次数的内容天窗口。非常感谢任何帮助!

2 个答案:

答案 0 :(得分:2)

将电子邮件/网络研讨会投射到单独的变量中。

dc <- dcast(dt, Name + ActivityDate ~ ActivityType, fun.aggregate=length)

然后应用下面的答案,在21天的窗口中获得每个人的滚动总和:

https://stackoverflow.com/a/24400600/2573061

答案 1 :(得分:1)

仅使用基数R并且看起来相对简单的方法可能看起来像:

# ensure that ActivityType is a factor
  dt$ActivityType <- factor(dt$ActivityType)   
# convert character dates to Date types
  dt$ActivityDate <- as.Date(dt$ActivityDate, "%m/%d/%Y")  
# ensure that dt is ordered by Name and ActivityDate
  dt <- dt[order(dt$Name, dt$ActivityDate),]
# for each ActivityDate and Name, count the number of instances within a 21 day window for each ActivityType
  dt <- cbind(dt, t(sapply(split(dt, list(dt$ActivityDate, dt$Name)), 
                         FUN=function(y) { z= (dt$Name == y$Name & dt$ActivityDate <= y$ActivityDate & dt$ActivityDate > (y$ActivityDate -21));
                                      sapply(levels(dt$ActivityType), FUN=function(x) sum(z & dt$ActivityType == x )) } )) )

这个版本比以前的版本更快,更简单。

<强>说明

我会尝试通过完成最后一个语句中的步骤来回答您的问题,尽管这会使答案有点长。我在你的问题中认为dt提供了两组重叠数据。首先,NameActivityDates列提供了需要结果的名称和日期集。其次,NameActivityDatesActivityTypes列提供用于计算结果的数据。所以,代码执行以下操作

  1. sapply split设置了所有Name-ActvityDates对的循环。在这种情况下,splitdt分成一个列表,其中列表的每个元素都是包含dt行的数据框。代码使用sapply-split,因为它将行作为数据框传递,所有列都包含在FUN参数中。代码不使用R包中的命名函数,而是使用通常的函数定义格式定义函数,即函数(参数){使用参数的语句}。在这种情况下,该函数没有名称,称为匿名函数。
  2. 现在我们已经设置了Name-AcivityDates对的循环,我们将以上述第二种方式考虑dt,作为用于计算每行所需结果的数据源。 dt。函数function(y)其中y是此函数的参数名称。 function(y)中的第一个语句标识dt的所有行,这些行与输入行Name的{​​{1}}和ActivityDate范围匹配(单行数据框) )其中yy$Name列,而Name是我们尝试匹配的y$ActivityDate范围的ActivityDate。此匹配过程的结果存储在ActivityDate中作为逻辑向量,其中z TRUE元素中的值i-th表示zi-th匹配dt,而值y表示不匹配。 FALSE可能包含多个z值的匹配项,但对于查找由ActivityType定义的dt行匹配的代码的性能非常重要每行ActivityDates仅执行一次。
  3. dt的第二个语句计算function(y)z的每个值的匹配数。 ActivityType遍历sapply的唯一值(例如电子邮件和网络研讨会),将每个值传递给匿名函数ActivityType,找到function(x)中匹配{{1}的行和dt形成一个逻辑向量,仅包含xz和日期范围的匹配项。 Name可以使用AcivityType,因为zfunction(x)的父环境function(y)中定义的function(x)sum作用于逻辑向量会计算TRUE个元素的数量,这些元素会提供结果中报告的计数。
  4. sapply返回一个矩阵,其中ActivityType为行,Name-ActivityDates为列,因此t用于构成转置,然后使用dt绑定cbind
  5. 我试图让代码具有良好的性能并且易于理解。如果需要进一步改进性能,可以使基本R代码更快一些,或者可以移动此方法以使用dplyrdata.table,这也可能有所帮助。如果您还有其他问题,请与我们联系。