Использование lapply с изменением аргументов - PullRequest
7 голосов
/ 06 июня 2011

R учебники продолжают пропагандировать использование lapply вместо циклов.Это легко даже для функций с аргументами типа

lapply(somelist, f, a=1, b=2) 

, но что если аргументы изменятся в зависимости от элемента списка?Предположим, мой список состоит из:

somelist$USA
somelist$Europe
somelist$Switzerland

плюс есть anotherlist с теми же регионами, и я хочу использовать lapply с этими изменяющимися аргументами?Это может быть полезно, когда f был, например, расчет коэффициента.

lapply(somelist, f, a= somelist$USA, b=anotherlist$USA) 

Есть ли способ, кроме цикла, эффективно проходить через эти регионы?

РЕДАКТИРОВАТЬ: моя проблема в том, что я пытался использовать ранее написанную функцию без индексов ...

ratio <-function(a,b){
z<-(b-a)/a
return(z)
}

, что привело к

lapply(data,ratio,names(data))

, что делаетне работа.Возможно, другие тоже могут извлечь уроки из этой ошибки.

Ответы [ 2 ]

16 голосов
/ 06 июня 2011

Применять поверх имен списков, а не элементов списка. E.g.:

somelist <- list('USA'=rnorm(10), 'Europe'=rnorm(10), 'Switzerland'=rnorm(10))
anotherlist <- list('USA'=5, 'Europe'=10, 'Switzerland'=4)
lapply(names(somelist), function(i) somelist[[i]] / anotherlist[[i]])

РЕДАКТИРОВАТЬ:

Вы также спрашиваете, существует ли способ «за исключением цикла» сделать это «эффективно». Вы должны отметить, что заявка не обязательно будет более эффективной. Эффективность, вероятно, будет зависеть от того, насколько быстро ваша внутренняя функция. Если вы хотите работать с каждым элементом списка, вам понадобится цикл, независимо от того, скрыт он в вызове apply () или нет. Проверьте этот вопрос: Является ли семейство R больше, чем синтаксический сахар?

Пример, который я привел выше, может быть переписан как цикл for, и вы можете сделать несколько наивных тестов:

fun1 <- function(){
    lapply(names(somelist), function(i) somelist[[i]] / anotherlist[[i]])
}
fun2 <- function(){
    for (i in names(somelist)){
        somelist[[i]] <- somelist[[i]] / anotherlist[[i]] 
    }
    return(somelist)
}
library(rbenchmark)

benchmark(fun1(), fun2(),
          columns=c("test", "replications",
          "elapsed", "relative"),
          order="relative", replications=10000)

Результат теста на моей машине был таким:

    test replications elapsed relative
1 fun1()        10000   0.145 1.000000
2 fun2()        10000   0.148 1.020690

Хотя это не настоящее рабочее приложение и функции не являются реалистичными задачами, вы можете видеть, что разница во времени вычислений весьма незначительна.

7 голосов
/ 06 июня 2011

Вам просто нужно разобраться, что надо lapply(). Здесь names() списков достаточно, после того как мы переписали f(), чтобы принять разные аргументы:

somelist <- list(USA = 1:10, Europe = 21:30,
                 Switzerland = seq(1, 5, length = 10))
anotherlist <- list(USA = list(a = 1, b = 2), Europe = list(a = 2, b = 4),
                    Switzerland = list(a = 0.5, b = 1))

f <- function(x, some, other) {
    (some[[x]] + other[[x]][["a"]]) * other[[x]][["b"]]
}

lapply(names(somelist), f, some = somelist, other = anotherlist)

Предоставление:

R> lapply(names(somelist), f, some = somelist, other = anotherlist)
[[1]]
 [1]  4  6  8 10 12 14 16 18 20 22

[[2]]
 [1]  92  96 100 104 108 112 116 120 124 128

[[3]]
 [1] 1.500000 1.944444 2.388889 2.833333 3.277778 3.722222 4.166667 4.611111
 [9] 5.055556 5.500000
...