Передача выражения в `MoreArgs` из`mapply`

Я занимаюсь программированием, используя dplyrи мне интересно, как передать выражение как (MoreArgs) аргумент к mapply?

Рассмотрим простую функцию F это подмножество data.frame основанный на некоторых ids и time_rangeзатем выводит сводную статистику на основе некоторого другого столбца x,

require(dplyr)
F <- function(ids, time_range, df, date_column, x) {
    date_column <- enquo(date_column)
    x <- enquo(x)
    df %>%
        filter(person_id %chin% ids) %>%
        filter(time_range[1] <= (!!date_column) & (!!date_column) <= time_range[2]) %>%
        summarise(newvar = sum(!!x))
}

Мы можем составить пример данных, к которым мы можем применить нашу функцию F,

person_ids <- lapply(1:2, function(i) sample(letters, size = 10))
time_ranges <- lapply(list(c("2014-01-01", "2014-12-31"),
                           c("2015-01-01", "2015-12-31")), as.Date)

require(data.table)
dt <- CJ(person_id = letters,
         date_col  = seq.Date(from = as.Date('2014-01-01'), to = as.Date('2015-12-31'), by = '1 day'))
dt[, z := rnorm(nrow(dt))]  # The variable we will later sum over, i.e. apply F to.

Мы можем успешно применять нашу функцию к каждому из наших входов.

F(person_ids[[1]], time_ranges[[1]], dt, date_col, z)
F(person_ids[[2]], time_ranges[[2]], dt, date_col, z)

И поэтому, если бы я хотел, я мог бы написать простой цикл for, чтобы решить мою проблему. Но если мы попытаемся применить синтаксический сахар и обернуть все в mapplyмы получаем ошибку.

mapply(F, ids = person_ids, time_range = time_ranges, MoreArgs = list(df = dt, date_column = date_col, x = z))

# Error in mapply... object 'date_col' not found

1 ответ

Решение

В mapply, MoreArgs предоставляется в виде списка, но R пытается оценить элементы списка, вызывая ошибку. Как подсказывает @Gregor, вы можете quote те MoreArgs что мы не хотим оценивать сразу, предотвращая ошибку и позволяя функции продолжаться. Это можно сделать с помощью базы quote или же dplyrquo:

mapply(F, person_ids, time_ranges, MoreArgs = list(dt, quote(date_col), quote(z)))

mapply(F, person_ids, time_ranges, MoreArgs = list(dt, quo(date_col), quo(z)))

Другой вариант заключается в использовании map2 от purrr пакет, который является tidyverse эквивалент mapply с двумя входными векторами. tidyverse функции настроены на работу с нестандартной оценкой, что позволяет избежать ошибок, с которыми вы получаете mapply без необходимости приводить аргументы:

library(purrr)

map2(person_ids, time_ranges, F, dt, date_col, z)
[[1]]
    newvar
1 40.23419

[[2]]
    newvar
1 71.42327

В целом, вы можете использовать pmap, который выполняет итерацию параллельно по любому количеству входных векторов:

pmap(list(person_ids, time_ranges), F, dt, date_col, z)
Другие вопросы по тегам