尝试使用 sapply 避免 for 循环(对于 gsub)
Trying to avoid for loop with sapply (for gsub)
尽量避免在以下代码中使用 for
循环,方法是利用 sapply
,如果可能的话。带循环的解决方案对我来说非常好,我只是想学习更多 R 并探索尽可能多的方法。
Objective:有一个向量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
,所以我不确定如何将 "inner" 隐式循环转换为矢量化解决方案。感谢任何帮助(即使是声明 - 这是不可能的)!
(我知道 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
循环提供了最佳时机,并且(在我看来)是最直接、最简单且可能最优雅的。坚持循环。
感谢大家。接受并赞成所有建议。
一种方法 - 优点是简洁但显然不是面向函数式编程 - 因为它在修改 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
,给出所需的结果。
这是顺序的,因此循环看起来很自然。这是一个几乎和 <<-
:
一样糟糕的解决方案
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"
真的,你应该使用循环。
sapply(seq_along(sf),function(x)i<-gsub(sf[x],rp[x],i))
尽量避免在以下代码中使用 for
循环,方法是利用 sapply
,如果可能的话。带循环的解决方案对我来说非常好,我只是想学习更多 R 并探索尽可能多的方法。
Objective:有一个向量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
,所以我不确定如何将 "inner" 隐式循环转换为矢量化解决方案。感谢任何帮助(即使是声明 - 这是不可能的)!
(我知道 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
循环提供了最佳时机,并且(在我看来)是最直接、最简单且可能最优雅的。坚持循环。
感谢大家。接受并赞成所有建议。
一种方法 - 优点是简洁但显然不是面向函数式编程 - 因为它在修改 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
,给出所需的结果。
这是顺序的,因此循环看起来很自然。这是一个几乎和 <<-
:
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"
真的,你应该使用循环。
sapply(seq_along(sf),function(x)i<-gsub(sf[x],rp[x],i))