【发布时间】:2015-07-26 07:30:59
【问题描述】:
尽量避免在以下代码中使用for 循环,尽可能使用sapply。带有循环的解决方案对我来说非常好,我只是想学习更多的 R 并探索尽可能多的方法。
目标:有一个向量i和两个向量sf(搜索)和rp(替换)。对于每个i 需要循环遍历sf 并替换为rp where match。
i = c("1 6 5 4","7 4 3 1")
sf = c("1","2","3")
rp = c("one","two","three")
funn <- function(i) {
for (j in seq_along(sf)) i = gsub(sf[j],rp[j],i,fixed=T)
return(i)
}
print(funn(i))
结果(正确):
[1] "one 6 5 4" "7 4 three one"
我也想做同样的事情,但使用sapply
#Trying to avoid a for loop in a fun
#funn1 <- function(i) {
# i = gsub(sf,rp,i,fixed=T)
# return(i)
#}
#print(sapply(i,funn1))
显然,上面注释的代码不起作用,因为我只能得到sf 的第一个元素。这是我第一次使用sapply,所以我不确定如何将“内部”隐式循环转换为矢量化解决方案。感谢您提供任何帮助(甚至是声明 - 这是不可能的)!
(我知道mgsub,但这不是这里的解决方案。想保留gsub)
编辑:包含软件包的完整代码以及以下提供的解决方案和时间:
#timing
library(microbenchmark)
library(functional)
i = rep(c("1 6 5 4","7 4 3 1"),10000)
sf = rep(c("1","2","3"),100)
rp = rep(c("one","two","three"),100)
#Loop
funn <- function(i) {
for (j in seq_along(sf)) i = gsub(sf[j],rp[j],i,fixed=T)
return(i)
}
t1 = proc.time()
k = funn(i)
t2 = proc.time()
#print(k)
print(microbenchmark(funn(i),times=10))
#mapply
t3 = proc.time()
mapply(function(u,v) i<<-gsub(u,v,i), sf, rp)
t4 = proc.time()
#print(i)
print(microbenchmark(mapply(function(u,v) i<<-gsub(u,v,i), sf, rp),times=10))
#Curry
t5 = proc.time()
Reduce(Compose, Map(function(u,v) Curry(gsub, pattern=u, replacement=v), sf, rp))(i)
t6 = proc.time()
print(microbenchmark(Reduce(Compose, Map(function(u,v) Curry(gsub, pattern=u, replacement=v), sf, rp))(i), times=10))
#4th option
n <- length(sf)
sf <- setNames(sf,1:n)
rp <- setNames(rp,1:n)
t7 = proc.time()
Reduce(function(x,j) gsub(sf[j],rp[j],x,fixed=TRUE),c(list(i),as.list(1:n)))
t8 = proc.time()
print(microbenchmark(Reduce(function(x,j) gsub(sf[j],rp[j],x,fixed=TRUE),c(list(i),as.list(1:n))),times=10))
#Usual proc.time
print(t2-t1)
print(t4-t3)
print(t6-t5)
print(t8-t7)
次:
Unit: milliseconds
expr min lq mean median uq max neval
funn(i) 143 143 149 145 147 165 10
Unit: seconds
expr min lq mean median uq max neval
mapply(function(u, v) i <<- gsub(u, v, i), sf, rp) 4.1 4.2 4.4 4.3 4.4 4.9 10
Unit: seconds
expr min lq mean median uq max neval
Reduce(Compose, Map(function(u, v) Curry(gsub, pattern = u, replacement = v), sf, rp))(i) 1.6 1.6 1.7 1.7 1.7 1.7 10
Unit: milliseconds
expr min lq mean median uq max neval
Reduce(function(x, j) gsub(sf[j], rp[j], x, fixed = TRUE), c(list(i), as.list(1:n))) 141 144 147 145 146 162 10
user system elapsed
0.15 0.00 0.15
user system elapsed
4.49 0.03 4.52
user system elapsed
1.68 0.02 1.68
user system elapsed
0.19 0.00 0.18
因此,在这种情况下,for 循环确实提供了最佳时机,并且(在我看来)是最直接、最简单且可能优雅的。坚持循环。
谢谢大家。所有建议都被接受并投票赞成。
【问题讨论】:
-
通常,您在所有要测试的东西上同时使用
microbenchmark。此外,如果这又是一个问题,您可能希望在未来的问题上使用performance进行标记。对了,盛林的回答居然用sapply。 -
我的目标是估计每个解决方案单独运行多长时间,以防替代解决方案比循环快得多。也点赞了盛林的回答