这就是我的数据框架的样子。最右边的两列是我想要的列。
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中出现网络研讨会或电子邮件的次数的内容天窗口。非常感谢任何帮助!
答案 0 :(得分:2)
将电子邮件/网络研讨会投射到单独的变量中。
dc <- dcast(dt, Name + ActivityDate ~ ActivityType, fun.aggregate=length)
然后应用下面的答案,在21天的窗口中获得每个人的滚动总和:
答案 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
提供了两组重叠数据。首先,Name
和ActivityDates
列提供了需要结果的名称和日期集。其次,Name
,ActivityDates
和ActivityTypes
列提供用于计算结果的数据。所以,代码执行以下操作
sapply
split
设置了所有Name-ActvityDates
对的循环。在这种情况下,split
将dt
分成一个列表,其中列表的每个元素都是包含dt
行的数据框。代码使用sapply-split
,因为它将行作为数据框传递,所有列都包含在FUN
参数中。代码不使用R包中的命名函数,而是使用通常的函数定义格式定义函数,即函数(参数){使用参数的语句}。在这种情况下,该函数没有名称,称为匿名函数。 Name-AcivityDates
对的循环,我们将以上述第二种方式考虑dt
,作为用于计算每行所需结果的数据源。 dt
。函数function(y)
其中y
是此函数的参数名称。 function(y)
中的第一个语句标识dt
的所有行,这些行与输入行Name
的{{1}}和ActivityDate
范围匹配(单行数据框) )其中y
是y$Name
列,而Name
是我们尝试匹配的y$ActivityDate
范围的ActivityDate
。此匹配过程的结果存储在ActivityDate
中作为逻辑向量,其中z
TRUE
元素中的值i-th
表示z
行i-th
匹配dt
,而值y
表示不匹配。 FALSE
可能包含多个z
值的匹配项,但对于查找由ActivityType
定义的dt
行匹配的代码的性能非常重要每行ActivityDates
仅执行一次。dt
的第二个语句计算function(y)
中z
的每个值的匹配数。 ActivityType
遍历sapply
的唯一值(例如电子邮件和网络研讨会),将每个值传递给匿名函数ActivityType
,找到function(x)
中匹配{{1}的行和dt
形成一个逻辑向量,仅包含x
,z
和日期范围的匹配项。 Name
可以使用AcivityType
,因为z
是function(x)
的父环境function(y)
中定义的function(x)
。 sum
作用于逻辑向量会计算TRUE
个元素的数量,这些元素会提供结果中报告的计数。sapply
返回一个矩阵,其中ActivityType
为行,Name-ActivityDates
为列,因此t
用于构成转置,然后使用dt
绑定cbind
。我试图让代码具有良好的性能并且易于理解。如果需要进一步改进性能,可以使基本R代码更快一些,或者可以移动此方法以使用dplyr
或data.table
,这也可能有所帮助。如果您还有其他问题,请与我们联系。