我创建了一些基本代码来实现我需要的东西,但由于它的代码很糟糕,因此速度非常慢。目的是从SE列中取一行,如果它与SC列匹配,则为它所属的每5分钟括号向计数器添加1。
我写的代码是
for (i in 1:(nrow(SC)))
for(j in 1:(nrow(SE)))
for (k in 0:5)
if ( (SE[j,3]==SC[i,1]) &
(as.POSIXlt(SE[j,1])>as.POSIXlt(SC[i,4]) +k*5*60)&
(as.POSIXlt(SE[j,1])<=as.POSIXlt(SC[i,4])+ (k+1)*5*60 ) &
(SE[j,2]==1) )
{ SC[i,6+k]=SC[i,6+k]+1 }
也就是说检查SC的每个单元格以查看条件(房间号是否相同,时间括号之间的时间等)。
这是非常低效的,因为三重循环在R中永远需要。寻找替换循环的方法,可能是向量还是应用?
> SE
UTC pin Room
1 2014-12-22 10:14:34 1 Alpha
4 2014-12-22 10:15:27 1 Alpha
5 2014-12-22 10:16:00 1 Alpha
8 2014-12-22 10:18:10 1 Alpha
12 2014-12-22 10:19:06 1 Alpha
13 2014-12-22 10:20:00 1 Alpha
14 2014-12-22 10:08:34 1 Beta
17 2014-12-22 10:15:29 1 Beta
18 2014-12-22 10:16:00 1 Beta
19 2014-12-22 10:17:00 1 Beta
22 2014-12-22 10:18:10 1 Beta
24 2014-12-22 10:19:00 1 Beta
26 2014-12-22 10:19:11 1 Beta
28 2014-12-22 10:09:34 1 Gamma
29 2014-12-22 10:39:11 1 Gamma
> SC
Room Capacity Video.Conference ST ET
1 Alpha 16 1 2014-12-22 10:00:00 2014-12-22 10:30:00
2 Alpha 16 1 2014-12-22 10:30:00 2014-12-22 11:00:00
3 Beta 16 1 2014-12-22 10:00:00 2014-12-22 10:30:00
4 Beta 16 1 2014-12-22 10:30:00 2014-12-22 11:00:00
5 Gamma 10 0 2014-12-22 10:00:00 2014-12-22 10:30:00
6 Gamma 10 0 2014-12-22 10:30:00 2014-12-22 11:00:00
>Desired #This is the intended output
X Room Capacity Vid ST ET X0.to.5.min X5.to.10.min X10.to.15.min X15.to.20.min X20.to.25.min X25.to.30.min
1 Alpha 16 1 2014-12-22 10:00:00 2014-12-22 10:30:00 0 0 1 5 0 0
2 Alpha 16 1 2014-12-22 10:30:00 2014-12-22 11:00:00 0 0 0 0 0 0
3 Beta 16 1 2014-12-22 10:00:00 2014-12-22 10:30:00 0 1 0 6 0 0
4 Beta 16 1 2014-12-22 10:30:00 2014-12-22 11:00:00 0 0 0 0 0 0
5 Gamma 10 0 2014-12-22 10:00:00 2014-12-22 10:30:00 0 1 0 0 0 0
6 Gamma 10 0 2014-12-22 10:30:00 2014-12-22 11:00:00 0 1 0 0 0 0
答案 0 :(得分:2)
此刻您的算法运行在O(n ^ 2),如果您对SE和SC列的属性一无所知,这是您可以做的最好的算法。 如果任一列中的数据具有某些特定属性,那么您可以进行一些优化。例如
SE中的条目是唯一的吗?如果是,那么你可以删除匹配 SC因此不会再次检查。
SE或SC中的条目是否排序?如果是,那么你可以使用 将SC中的搜索短路的比较(例如:如果SC被排序 按顺序增加,然后在检查匹配时,如果行I&#m; m 比较大于我搜索的时间然后我保释 因为不再有比赛了)
答案 1 :(得分:0)
你可以减少for循环中的函数调用次数(特别是as.POSIXlt
调用),这应该有所帮助。此外,&&
运算符可能会更快,因为在第一个计算结果为false的条件之后,将不会评估后续比较。
posix.SE <- as.POSIXlt(SE[,1])
posix.SC <- as.POSIXlt(SC[,4])
for (i in 1:(nrow(SC)))
for(j in 1:(nrow(SE)))
for (k in (0:5))
if ( (SE[j,3]==SC[i,1]) &&
(posix.SE[j]>posix.SC[i] + k*300)&&
(posix.SE[j]<=posix.SC[i]+ (k+1)*300 ) &&
(SE[j,2]==1) ) {
SC[i,6+k]=SC[i,6+k]+1
}
您还可以通过以下方式减少评估if
子句中第一个条件的次数:
for(val in unique(SE[,3]))
for(i in which(SC[,1] == val))
for(j in which(SE[,3] == val))
for (k in (0:5))
if ((posix.SE[j]>posix.SC[i] + k*300)&&
(posix.SE[j]<=posix.SC[i]+ (k+1)*300 ) &&
(SE[j,2]==1) ) {
SC[i,6+k]=SC[i,6+k]+1
}
使用&#39;外部&#39;
可能更有效率for(val in unique(SC[,1])){
# index the relevent rows for each value in SC[,1]
index.SC <- which(SC[,1] == val)
index.SE <- which(SE[,3] == val & SE[,2]==1)
MX <- outer(posix.SE[index.SE], posix.SC[index.SC],`-`)
for (k in (0:5))
SC[indxe.SC,6+k] <- apply((MX > k*300)& (MX <= (k+1)*300 ),2,sum)
}
[如果SC [,1]是您想要在for循环中使用&#39;级别(SC [,1])而不是唯一(SC [,1])的因素... ]
答案 2 :(得分:0)
进一步考虑@ jthorpe的建议,在可能的情况下进行矢量化并进行常规计算
step <- 5 * 60
se <- as.POSIXlt(SE[,1]) / step
sc <- as.POSIXlt(SC[,4]) / step
k <- 0:5
更新data.frame非常昂贵,因此请创建一个包含答案的矩阵
ans <- as.matrix(SC[, 6 + 0:5])
重新排列循环,以便可以对第一个和最后一个测试标准的计算进行矢量化
for (j in seq_along(se)[SE[,2] == 1])
for (i in seq_along(sc)[SE[j, 3] == SC[,1]])
并矢量化最里面的循环
{
d <- se[j] - sc[i]
idx <- k[(d > k) & (d <= (k + 1))] + 1
ans[i, idx] <- ans[i, idx] + 1
}
这仍然是二次时间算法,正如@hhafez所指出的那样,使用数据属性可能还有相当大的改进空间。