【问题标题】:Trying to avoid for loop with sapply (for gsub)尝试使用 sapply 避免 for 循环(对于 gsub)
【发布时间】: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
  • 我的目标是估计每个解决方案单独运行多长时间,以防替代解决方案比循环快得多。也点赞了盛林的回答

标签: r for-loop gsub sapply


【解决方案1】:

这是顺序的,所以循环看起来很自然。这是一个几乎和&lt;&lt;-一样糟糕的解决方案:

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"

真的,你应该使用循环。

【讨论】:

  • 嗯 .. 不能接受 > 1 个答案。虽然赞成。谢谢!
【解决方案2】:
sapply(seq_along(sf),function(x)i<-gsub(sf[x],rp[x],i))

【讨论】:

  • 你能添加你看到的输出吗?对我来说,它看起来不像OP。尝试在末尾添加[,1]
【解决方案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替换为one2替换为two等。然后将这些函数组合并应用于i,给出想要的结果。

【讨论】:

  • 不,我只显示i 而不是mapply 的结果!
  • 好的,我错过了评论的那部分
  • np,我添加了一些“理智”的解决方案。
猜你喜欢
  • 2018-05-26
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多