试图避免与sapply循环(对于gsub)

时间:2015-05-14 15:39:50

标签: r for-loop gsub sapply

尽可能使用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循环提供了最佳时机,并且(在我看来)最简单,最简单,也可能是优雅的。坚持循环。

感谢所有人。所有建议都被接受和投票。

3 个答案:

答案 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))