如何矢量化一个循环,对于每一行,对 r 中该实体 ID 的当前条目和所有先前条目之间经过的时间的函数求和
How to vectorize a loop that, for each row, sums a function of the time elapsed between the current and all prior entries of that entity ID in r
我有一个很大的 data.table(大约 90 万行),可以用以下示例表示:
row.id entity.id event.date result
1: 1 100 2015-01-20 NA
2: 2 101 2015-01-20 NA
3: 3 104 2015-01-20 NA
4: 4 107 2015-01-20 NA
5: 5 103 2015-01-23 NA
6: 6 109 2015-01-23 NA
7: 7 102 2015-01-23 NA
8: 8 101 2015-01-26 NA
9: 9 110 2015-01-26 NA
10: 10 112 2015-01-26 NA
11: 11 109 2015-01-26 NA
12: 12 130 2015-01-29 NA
13: 13 100 2015-01-29 NA
14: 14 127 2015-01-29 NA
15: 15 101 2015-01-29 NA
16: 16 119 2015-01-29 NA
17: 17 104 2015-02-03 NA
18: 18 101 2015-02-03 NA
19: 19 125 2015-02-03 NA
20: 20 130 2015-02-03 NA
基本上我的列包含:代表相关实体的 ID (entity.id);此 ID 参与的事件的日期(请注意,许多不同数量的实体将参与每个事件)。我需要计算一个因子,对于每个事件日期的每个 entity.id,该因子(非线性地)取决于自输入该实体 ID 的所有先前事件以来经过的时间(以天为单位)。
换句话说,在 data.table 的每一行上,我需要找到所有具有匹配 ID 且日期早于相关行的事件日期的实例,计算出“当前”事件和历史事件之间的时间差(以天为单位),并对应用于每个时间段的一些非线性函数求和(在此示例中我将使用正方形)。
在上面的示例中,对于 2015 年 3 月 2 日(第 18 行)的 entity.id = 101,我们需要回顾该 ID 在第 15、8 和 2 行的先前条目,计算与“当前”事件(14、8 和 5 天)的天数差异,然后通过对这些时间段的平方求和来计算答案 (14^2 + 8^2 + 5^2) = 196 + 64 + 25 = 285。(实际函数稍微复杂一些,但这已足够具有代表性。)
使用 for 循环很容易实现,如下所示:
# Create sample dt
dt <- data.table(row.id = 1:20,
entity.id = c(100, 101, 104, 107, 103, 109, 102, 101, 110, 112,
109, 130, 100, 127, 101, 119, 104, 101, 125, 130),
event.date = as.Date(c("2015-01-20", "2015-01-20", "2015-01-20", "2015-01-20",
"2015-01-23", "2015-01-23", "2015-01-23",
"2015-01-26", "2015-01-26", "2015-01-26", "2015-01-26",
"2015-01-29", "2015-01-29", "2015-01-29", "2015-01-29", "2015-01-29",
"2015-02-03", "2015-02-03", "2015-02-03", "2015-02-03")),
result = NA)
setkey(dt, row.id)
for (i in 1:nrow(dt)) { #loop through each entry
# get a subset of dt comprised of rows with this row's entity.id, which occur prior to this row
event.history <- dt[row.id < i & entity.id == entity.id[i]]
# calc the sum of the differences between the current row event date and the prior events dates, contained within event.history, squared
dt$result[i] <- sum( (as.numeric(dt$event.date[i]) - as.numeric(event.history$event.date)) ^2 )
}
不幸的是,在真实数据集上它也非常慢,这无疑是因为如果需要大量的子集操作。有没有办法矢量化或加速此操作?我已经搜索和搜索并绞尽脑汁,但无法弄清楚如何在不循环的情况下根据每行的不同数据对行进行 vecotrally 子集化。
请注意,我创建了一个 row.id 列以允许我提取所有之前的行(而不是之前的日期),因为这两者大致相同(一个实体一天不能参加一个以上的活动)和这种方式要快得多(我认为因为它避免了在进行比较之前将日期强制转换为数字的需要,即 Dt[as.numeric(event_date) < as.numeric(event_date[i])]
另请注意,我并不拘泥于成为 data.table;如果需要,我很乐意使用 dplyr 或其他机制来实现这一点。
我认为这可以使用具有适当的非相等连接条件的自连接来实现:
dt[, result2 := dt[
dt,
on=c("entity.id","event.date<event.date"),
sum(as.numeric(x.event.date - i.event.date)^2), by=.EACHI]$V1
]
dt
这给出了与循环输出匹配的结果,NA
值除外:
# row.id entity.id event.date result result2
# 1: 1 100 2015-01-20 0 NA
# 2: 2 101 2015-01-20 0 NA
# 3: 3 104 2015-01-20 0 NA
# 4: 4 107 2015-01-20 0 NA
# 5: 5 103 2015-01-23 0 NA
# 6: 6 109 2015-01-23 0 NA
# 7: 7 102 2015-01-23 0 NA
# 8: 8 101 2015-01-26 36 36
# 9: 9 110 2015-01-26 0 NA
#10: 10 112 2015-01-26 0 NA
#11: 11 109 2015-01-26 9 9
#12: 12 130 2015-01-29 0 NA
#13: 13 100 2015-01-29 81 81
#14: 14 127 2015-01-29 0 NA
#15: 15 101 2015-01-29 90 90
#16: 16 119 2015-01-29 0 NA
#17: 17 104 2015-02-03 196 196
#18: 18 101 2015-02-03 285 285
#19: 19 125 2015-02-03 0 NA
#20: 20 130 2015-02-03 25 25
我有一个很大的 data.table(大约 90 万行),可以用以下示例表示:
row.id entity.id event.date result
1: 1 100 2015-01-20 NA
2: 2 101 2015-01-20 NA
3: 3 104 2015-01-20 NA
4: 4 107 2015-01-20 NA
5: 5 103 2015-01-23 NA
6: 6 109 2015-01-23 NA
7: 7 102 2015-01-23 NA
8: 8 101 2015-01-26 NA
9: 9 110 2015-01-26 NA
10: 10 112 2015-01-26 NA
11: 11 109 2015-01-26 NA
12: 12 130 2015-01-29 NA
13: 13 100 2015-01-29 NA
14: 14 127 2015-01-29 NA
15: 15 101 2015-01-29 NA
16: 16 119 2015-01-29 NA
17: 17 104 2015-02-03 NA
18: 18 101 2015-02-03 NA
19: 19 125 2015-02-03 NA
20: 20 130 2015-02-03 NA
基本上我的列包含:代表相关实体的 ID (entity.id);此 ID 参与的事件的日期(请注意,许多不同数量的实体将参与每个事件)。我需要计算一个因子,对于每个事件日期的每个 entity.id,该因子(非线性地)取决于自输入该实体 ID 的所有先前事件以来经过的时间(以天为单位)。
换句话说,在 data.table 的每一行上,我需要找到所有具有匹配 ID 且日期早于相关行的事件日期的实例,计算出“当前”事件和历史事件之间的时间差(以天为单位),并对应用于每个时间段的一些非线性函数求和(在此示例中我将使用正方形)。
在上面的示例中,对于 2015 年 3 月 2 日(第 18 行)的 entity.id = 101,我们需要回顾该 ID 在第 15、8 和 2 行的先前条目,计算与“当前”事件(14、8 和 5 天)的天数差异,然后通过对这些时间段的平方求和来计算答案 (14^2 + 8^2 + 5^2) = 196 + 64 + 25 = 285。(实际函数稍微复杂一些,但这已足够具有代表性。)
使用 for 循环很容易实现,如下所示:
# Create sample dt
dt <- data.table(row.id = 1:20,
entity.id = c(100, 101, 104, 107, 103, 109, 102, 101, 110, 112,
109, 130, 100, 127, 101, 119, 104, 101, 125, 130),
event.date = as.Date(c("2015-01-20", "2015-01-20", "2015-01-20", "2015-01-20",
"2015-01-23", "2015-01-23", "2015-01-23",
"2015-01-26", "2015-01-26", "2015-01-26", "2015-01-26",
"2015-01-29", "2015-01-29", "2015-01-29", "2015-01-29", "2015-01-29",
"2015-02-03", "2015-02-03", "2015-02-03", "2015-02-03")),
result = NA)
setkey(dt, row.id)
for (i in 1:nrow(dt)) { #loop through each entry
# get a subset of dt comprised of rows with this row's entity.id, which occur prior to this row
event.history <- dt[row.id < i & entity.id == entity.id[i]]
# calc the sum of the differences between the current row event date and the prior events dates, contained within event.history, squared
dt$result[i] <- sum( (as.numeric(dt$event.date[i]) - as.numeric(event.history$event.date)) ^2 )
}
不幸的是,在真实数据集上它也非常慢,这无疑是因为如果需要大量的子集操作。有没有办法矢量化或加速此操作?我已经搜索和搜索并绞尽脑汁,但无法弄清楚如何在不循环的情况下根据每行的不同数据对行进行 vecotrally 子集化。
请注意,我创建了一个 row.id 列以允许我提取所有之前的行(而不是之前的日期),因为这两者大致相同(一个实体一天不能参加一个以上的活动)和这种方式要快得多(我认为因为它避免了在进行比较之前将日期强制转换为数字的需要,即 Dt[as.numeric(event_date) < as.numeric(event_date[i])]
另请注意,我并不拘泥于成为 data.table;如果需要,我很乐意使用 dplyr 或其他机制来实现这一点。
我认为这可以使用具有适当的非相等连接条件的自连接来实现:
dt[, result2 := dt[
dt,
on=c("entity.id","event.date<event.date"),
sum(as.numeric(x.event.date - i.event.date)^2), by=.EACHI]$V1
]
dt
这给出了与循环输出匹配的结果,NA
值除外:
# row.id entity.id event.date result result2
# 1: 1 100 2015-01-20 0 NA
# 2: 2 101 2015-01-20 0 NA
# 3: 3 104 2015-01-20 0 NA
# 4: 4 107 2015-01-20 0 NA
# 5: 5 103 2015-01-23 0 NA
# 6: 6 109 2015-01-23 0 NA
# 7: 7 102 2015-01-23 0 NA
# 8: 8 101 2015-01-26 36 36
# 9: 9 110 2015-01-26 0 NA
#10: 10 112 2015-01-26 0 NA
#11: 11 109 2015-01-26 9 9
#12: 12 130 2015-01-29 0 NA
#13: 13 100 2015-01-29 81 81
#14: 14 127 2015-01-29 0 NA
#15: 15 101 2015-01-29 90 90
#16: 16 119 2015-01-29 0 NA
#17: 17 104 2015-02-03 196 196
#18: 18 101 2015-02-03 285 285
#19: 19 125 2015-02-03 0 NA
#20: 20 130 2015-02-03 25 25