我有一个数据框,其中一列是时间戳列表。我需要注释哪些时间戳是有效的,取决于它们是否足够接近(即,在1秒内)到另一个有效时间戳列表的元素。为此,我有一个辅助功能。
valid_times <- c(219.934, 229.996, 239.975, 249.935, 259.974, 344)
actual_times <- c(200, 210, 215, 220.5, 260)
strain <- c("green", "green", "green", "green", "green", "green")
valid_or_not <- c(rep("NULL", 6))
df <- data.frame(strain, actual_times, valid_or_not)
我的数据框架如下所示:
strain actual_times valid_or_not
1 green 200.0 NULL
2 green 210.0 NULL
3 green 215.0 NULL
4 green 220.5 NULL
5 green 260.0 NULL
我的助手(检查实际时间是否在有效时间的1秒内)如下:
valid_or_not_fxn<- function(actual_time){
c = "not valid"
for (i in 1:length(valid_times))
if (abs(valid_times[i] - actual_time) <= 1) {
c <- "valid"
} else {
}
return(c)
}
我试图做的是使用带有此辅助函数的for循环遍历整个数据框。
然而......它真的很慢(在我的真实数据集上),因为它是一个嵌套循环交叉比较两个100个元素长的列表。 我无法理解这一点。
df$valid_or_not <- as.character(df$valid_or_not)
for (i in 1:nrow(df))
print(df[i, "valid_or_not"])
df[i, "valid_or_not"] <- valid_or_not_fxn(df[i, "actual_times"])
感谢您的帮助!
答案 0 :(得分:1)
无论你做什么,你基本上都必须至少进行length(valid_times)
次比较。可能更好地循环valid_times
并将该向量的每个项目作为向量化操作与actual_times
列进行比较。这样你就只有5次循环迭代。
这样做的一种方法是:
df$test <- Reduce(`|`, lapply(valid_times, function(x) abs(df$actual_times - x) <= 1))
# strain actual_times valid_or_not
#1 green 200.0 FALSE
#2 green 210.0 FALSE
#3 green 215.0 FALSE
#4 green 220.5 TRUE
#5 green 260.0 TRUE
df
中的100K行和1000 valid_times
次测试在<4秒内完成:
df2 <- df[sample(1:5,1e5,replace=TRUE),]
valid_times2 <- valid_times[sample(1:5,1000,replace=TRUE)]
system.time(Reduce(`|`, lapply(valid_times2, function(x) abs(df2$actual_times - x) <= 1)))
# user system elapsed
# 3.13 0.40 3.54
答案 1 :(得分:1)
这种简单的方法是避免数据帧操作。因此,您可以执行此检查并填充valid_or_not向量,然后将它们组合到数据框中:
valid_or_not[sapply(actual_times, function(x) any(abs(x - valid_times) <= 1))] <- "valid"
注意,通过该行,valid_or_not向量被索引为具有相等长度的布尔值向量(条件是否满足,T或F)。因此,仅更新向量中的TRUE值索引。 valid_or_not和actual_times向量必须具有相同的长度,而valid_times向量的长度可以不同。
顺便说一句,“plying”for循环不会显着提高性能,因为它只是“for”循环的“包装器”。只有性能的提高来自避免由于整洁的中间对象和更简洁的代码风格,并在某些情况下避免冗余复制。对于Vectorize函数也是如此:它只包含通过函数的for循环,例如“outer”函数,FUN必须以这种方式“向量化”。实际上它并没有给出真正矢量化操作的性能。在我的例子中,性能增强来自于使用“any”函数替换for循环。
由于某种“错误”,子集化数据帧有一个重要的损失。正如Hadley Wickham在Advanced-R的Performance主题中所解释的那样:
从数据框中提取单个值
以下微基准测试显示了五种访问单个值的方法 (来自右下角的数字)来自内置的mtcars 数据集。性能的变化令人吃惊:最慢的方法 比最快的时间长30倍。没有理由必须这样做 在性能方面有如此巨大的差异。简直就是没有人拥有 有时间解决它。
microbenchmark( "[32, 11]" = mtcars[32, 11], "$carb[32]" = mtcars$carb[32], "[[c(11, 32)]]" = mtcars[[c(11, 32)]], "[[11]][32]" = mtcars[[11]][32], ".subset2" = .subset2(mtcars, 11)[32] ) ## Unit: nanoseconds ## expr min lq mean median uq max neval ## [32, 11] 15,300 16,300 18354 17,000 17,800 76,400 100 ## $carb[32] 8,860 9,930 12836 10,600 11,600 85,400 100 ## [[c(11, 32)]] 7,200 8,110 9293 8,780 9,350 21,300 100 ## [[11]][32] 6,330 7,580 8377 8,100 8,690 20,900 100 ## .subset2 334 566 4461 669 800 368,000 100
对数据帧进行子集化的最有效方法是使用.subset2方法。你的糟糕表现主要归功于这个事实。
如前所述:
@thelatemail工作解决方案的补充:
一些基准测试显示:
纯粹的“任何”和|比较:
> microbenchmark(any(T,F,F,F,F,F), T|F|F|F|F|F)
Unit: nanoseconds
expr min lq mean median uq max neval cld
any(T, F, F, F, F, F) 274 307.0 545.86 366.5 429.5 16380 100 a
T | F | F | F | F | F 597 626.5 903.47 668.5 730.0 18966 100 a
Pure“Reduce”和矢量化比较:
> vec0 <- rep(1, 1e6)
> microbenchmark(Reduce("+", vec0), sum(vec0), times = 10)
Unit: microseconds
expr min lq mean median uq
Reduce("+", vec0) 308415.064 310071.953 318503.6048 312940.6355 317648.354
sum(vec0) 930.625 936.775 944.2416 943.5425 949.257
max neval cld
369864.993 10 b
962.349 10 a
减少“|”与矢量化的“任何”比较(对于极端情况)。 “任何”节拍超过1e5次:
> vec1 <- c(T, rep(F, 1e6))
> microbenchmark(Reduce("|", vec1), any(vec1), times = 10)
Unit: nanoseconds
expr min lq mean median uq
Reduce("|", vec1) 394040518 395792399 402703632.6 399191803 400990304
any(vec1) 154 267 1932.5 2588 2952
max neval cld
441805451 10 b
3420 10 a
当单个TRUE位于最后(所以“任何”不再是懒惰并且必须检查整个向量),“任何”仍然超过400次:
> vec2 <- c(rep(F, 1e6), T)
> microbenchmark(Reduce("|", vec2), any(vec2), times = 10)
Unit: microseconds
expr min lq mean median uq
Reduce("|", vec2) 396625.318 401744.849 416732.5087 407447.375 424538.222
any(vec2) 736.975 787.047 857.5575 832.137 926.076
max neval cld
482116.632 10 b
1013.732 10 a