2015-05-14 3 views
3

Попытка избежать использования цикла for в следующем коде, используя sapply, если это вообще возможно. Решение с loop работает отлично для меня, я просто пытаюсь узнать больше 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.)

EDIT: Полный код с пакетами и 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

Моя цель состояла в том, чтобы оценить, как долго каждое решение выполняется отдельно, в случае, если альтернативное решение намного быстрее, чем цикл. Ответ на предыдущий ответ Шенглин тоже –

ответ

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" 

Что is is is Map(function(u,v) Curry(gsub, pattern=u, replacement=v), sf, rp) строит список функций, которые соответственно заменят 1 на one, 2 с two, e tc. Затем эти функции составлены и применены к i, что дает желаемый результат.

+0

нет Я просто показываю 'i', а не результат' 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

Hmm .. не можем принять> 1 ответ. Приобретено. Благодаря! –

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

Не могли бы вы добавить вывод, который видите? Это не похоже на OP для меня. Попробуйте добавить '[, 1]' в конец. – Frank