2015-06-10 4 views
21

В отношении this question, я пытался выяснить простейший способ применения списка функций к списку значений. В принципе, вложенный lapply. Например, здесь мы применяем sd и mean встраиваемым набора данных trees:Применить список функций к списку значений

funs <- list(sd=sd, mean=mean) 
sapply(funs, function(x) sapply(trees, x)) 

получить:

   sd  mean 
Girth 3.138139 13.24839 
Height 6.371813 76.00000 
Volume 16.437846 30.17097 

Но я надеялся, чтобы избежать внутреннего function и иметь что-то вроде:

sapply(funs, sapply, X=trees) 

который не работает, потому что X соответствует первому sapply вместо второго. Мы можем сделать это с functional::Curry:

sapply(funs, Curry(sapply, X=trees)) 

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

+5

hadley написал целую главу на эту тему: http://adv-r.had.co.nz/Functional-programming.html#lists-of-functions, так как я не более умный, чем я. не лучший способ сделать это – grrgrrbla

+0

Не проще, но приятно, если вы хотите аккуратный data.frame в конце: 'library (purrr); map_df (funs, ~ map_df (деревья, .x), .id = 'statistic') ' – alistaire

ответ

18

Поскольку mapply использование многоточие ... передать векторы (Atomics или списки), а не по имени аргумента (X), как и в sapply, lapply, etc ... вам не нужно назвать параметр X = trees, если вы используете mapply вместо of sapply:

funs <- list(sd = sd, mean = mean) 

x <- sapply(funs, function(x) sapply(trees, x)) 

y <- sapply(funs, mapply, trees) 

> y 
       sd  mean 
Girth 3.138139 13.24839 
Height 6.371813 76.00000 
Volume 16.437846 30.17097 
> identical(x, y) 
[1] TRUE 

Вы были за одно письмо, чтобы получить то, что искали! :)

Обратите внимание, что я использовал список для funs, потому что я не могу создать DataFrame функций, я получил ошибку.

> R.version.string 
[1] "R version 3.1.3 (2015-03-09)" 
+3

Очень умный, определенно будет использовать это в будущем; Я считаю, что ключевой особенностью является то, что 'mapply' принимает аргумент функции как первый аргумент, так что все это работает. – BrodieG

13

Вы в основном нуждаетесь в какой-либо анонимной функции, потому что не было другого способа различать именованные параметры для двух разных вызовов sapply. Вы уже указали явную анонимную функцию и метод Curry. Вы можете также использовать magrittr

library(magrittr) 
sapply(funs, . %>% sapply(trees, .)) 
# or .. funs %>% sapply(. %>% sapply(trees, .)) 

, но дело в том, что вам нужно что-то там делать расщепление. «Проблема» заключается в том, что sapply отправляет lapply, который является internal function, который, как представляется, помещает изменяемые значения в начало вызова функции. Вам нужно что-то изменить параметры, и из-за идентичных наборов имен параметров невозможно дразнить это отдельно, без вспомогательной функции, чтобы заботиться об устранении неоднозначности.

Функция mapply позволяет вам передавать список в «MoreArgs», который позволяет обойти конфликт с именованным параметром. Это предназначено для разделения между элементами, которые вы должны векторизовать, и теми, которые исправлены. Таким образом, вы можете сделать

mapply(sapply, funs, MoreArgs=list(X=trees)) 
#    sd  mean 
# Girth 3.138139 13.24839 
# Height 6.371813 76.00000 
# Volume 16.437846 30.17097 
+1

Приятный с' MoreArgs'. Я думаю, 'magrittr' может быть' funs%>% sapply (.%>% Sapply (X = деревья)) '? Определенно было немного двойного приема, видя '.' в качестве первого элемента в трубе. – BrodieG

+1

Да, я добавил, что, хотя я думаю, что первая версия более понятна. Честно говоря, я считаю, что лучший способ - просто использовать явную анонимную функцию, как вы делали в первый раз: 'sapply (funs, function (x) sapply (trees, x))' – MrFlick

+0

согласован; Я снова отредактировал, чтобы удалить дополнительный '.', но не на 100% уверен, что следую своей логике ... – BrodieG

0

Хотя не столь поучительным, ни как элегантный, как решение, представленное на @ Floo0, вот еще один дубль, используя tidyr и dplyr:

library(dplyr) 
library(tidyr) 

fns <- funs(sd = sd, mean = mean) 
trees %>% 
    gather(property, value, everything()) %>% 
    group_by(property) %>% 
    summarise_all(fns) 

# A tibble: 3 x 3 
# property  sd  mean 
#  <chr>  <dbl> <dbl> 
# 1 Girth 3.138139 13.24839 
# 2 Height 6.371813 76.00000 
# 3 Volume 16.437846 30.17097 

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

5

Другой подход с использованием purrr будет:

require(purrr) 

funs <- list(sd=sd, mean=mean) 
trees %>% map_df(~invoke_map(funs, ,.), .id="id") 

Важно: Обратите внимание на пустой второй аргумент invoke_map в соответствии положением. См. Примеры ?purrr::invoke_map.

, который дает вам:

Source: local data frame [3 x 3] 

     id  sd  mean 
    <chr>  <dbl> <dbl> 
1 Girth 3.138139 13.24839 
2 Height 6.371813 76.00000 
3 Volume 16.437846 30.17097 

Вместо rownames этот подход дает столбец id, содержащий исходные столбцы.

+0

При использовании purrr 0.2.2 (и, возможно, более ранних версий, а также - я не проверял), для получения результата нужно использовать 'invoke_map_df()', а не 'invoke_map()'. – egnha

+0

@egnha, это странно. Для меня это отлично работает с 'purrr_0.2.2'. Использование 'invoke_map_df' приводит к ошибке: невозможно преобразовать объект в кадр данных ... Какую версию R вы используете? – Rentrop

+0

Это недоумение. Я использую R 3.3.0; запускал код в новом сеансе с purrr (и без файлов инициализации, загруженных R). Морально, 'invoke_map_df' является правильной' invoke_map * 'для применения (и корректно работает на моей машине), так как 'map_df' создает фрейм данных путем привязки строк (если только я не понял что-то). – egnha

Смежные вопросы