尽可能使用for
,尽量避免在以下代码中使用sapply
循环。带循环的解决方案对我来说非常好,我只是想学习更多R并尽可能多地探索方法。
目标:有一个向量i
和两个向量sf
(搜索)和rp
(替换)。每个i
需要循环sf
并替换为匹配的rp
。
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
循环提供了最佳时机,并且(在我看来)最简单,最简单,也可能是优雅的。坚持循环。
感谢所有人。所有建议都被接受和投票。
答案 0 :(得分:3)
一种方法 - 优点是简洁但显然不是面向函数编程 - 因为它在修改i
时具有边界效应:
mapply(function(u,v) i<<-gsub(u,v,i), sf, rp)
#> i
#[1] "one 6 5 4" "7 4 three one"
或者这是一种纯函数式编程方法:
library(functional)
Reduce(Compose, Map(function(u,v) Curry(gsub, pattern=u, replacement=v), sf, rp))(i)
#[1] "one 6 5 4" "7 4 three one"
Map(function(u,v) Curry(gsub, pattern=u, replacement=v), sf, rp)
构建了一个功能列表,它将分别用1
替换one
,用2
替换two
等等。然后这些函数组成并应用于i
,从而得到所需的结果。
答案 1 :(得分:2)
这是顺序的,所以循环似乎很自然。这是一个几乎和<<-
一样糟糕的解决方案:
n <- length(sf)
Reduce(function(x,j) gsub(sf[j],rp[j],x,fixed=TRUE),c(list(i),as.list(1:n)))
# [1] "one 6 5 4" "7 4 three one"
真的,你应该使用循环。
答案 2 :(得分:2)
sapply(seq_along(sf),function(x)i<-gsub(sf[x],rp[x],i))