2015-05-14 65 views
3

試圖避免使用在下面的代碼for循環利用sapply循環,如果在所有可能的。循環解決方案對我來說工作得很好,我只是想學習更多的R並儘可能多地探索。試圖避免與sapply(對於GSUB)

目標:有一個載體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。)

編輯:該包裝完整的代碼和belowoffered解決方案和時間安排:

#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循環提供了最佳時機,是(在我看來)最straightforwar d,簡單,可能優雅。堅持循環。

感謝所有。所有建議都被接受並提升。

+1

通常,您在同時測試的所有東西上使用'microbenchmark'。另外,如果再次擔心這一問題,您可能希望在未來的問題上使用「效果」標籤。順便說一下,盛​​林的答案實際上是使用'sapply'。 – Frank

+0

我的目標是估計每個解決方案分別運行多久,以防備用解決方案比循環快得多。 upvoted盛林的回答也是 –

回答

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)建立將與two,e爲one分別替換12的功能列表然後這些函數被組合並應用於i,從而得到期望的結果。

+0

不,我只是顯示'我',然後不是'mapply'的結果! –

+0

好的,我錯過了那部分,因爲它被評論 – akrun

+0

np,我添加了一些'理智'的解決方案。 –

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" 

真的,你應該使用一個循環。

+1

嗯..不能接受> 1個答案。儘管如此。謝謝! –

2
sapply(seq_along(sf),function(x)i<-gsub(sf[x],rp[x],i)) 
+1

你可以添加你看到的輸出嗎?它看起來不像OP對我來說很像。嘗試在末尾添加'[,1]'。 – Frank