Использование dplyr для применения функции нескольких столбцов фрейма данных R

Используя «глаголы» dplyr, как я могу применить (общую) функцию к столбцу фрейма данных R, если эта функция зависит от нескольких столбцов фрейма данных?

Вот конкретный пример ситуации, с которой я сталкиваюсь. У меня есть такой фрейм данных:

df <- data.frame(
    d1 = c('2016-01-30 08:40:00 UTC', '2016-03-06 09:30:00 UTC'),
    d2 = c('2016-01-30 16:20:00 UTC', '2016-03-06 13:20:00 UTC'),
    tz = c('America/Los_Angeles', 'America/Chicago'), stringsAsFactors = FALSE)

Я хочу преобразовать время UTC в местное время, чтобы получить такой кадр данных:

                   d1                  d2                  tz
1 2016-01-30 00:40:00 2016-01-30 08:20:00 America/Los_Angeles
2 2016-03-06 03:30:00 2016-03-06 07:20:00     America/Chicago

Для этого я хотел бы применить следующую функцию, которая преобразует время UTC в местное время с помощью библиотеки lubridate, к столбцам даты:

getLocTime <- function(d, tz) {
    as.character(with_tz(ymd_hms(d), tz))
}

Используя dplyr, кажется, что преобразование

df %>% mutate(d1 = getLocTime(d1, tz), d2 = getLocTime(d2, tz))

должен сделать трюк. Однако это не удается с жалобой Error in eval(expr, envir, enclos): invalid 'tz' value.

Единственный способ, которым мне удалось выполнить преобразование в местное время, - это довольно неуклюжее назначение

df[c('d1', 'd2')] <- lapply(c('d1', 'd2'),
                            function(x) unlist(Map(getLocTime, df[[x]], df$tz)))

Существует ли естественный способ выполнить это преобразование с помощью идиом dplyr?


person egnha    schedule 09.05.2016    source источник
comment
Это вызывает getLocTime(c("2016-01-30 08:40:00 UTC", "2016-03-06 09:30:00 UTC"), c("America/Los_Angeles", "America/Chicago")) для 1-го mutate, что не работает. Вы можете векторизовать свою функцию, например. vgetLocTime <- Vectorize(getLocTime, c("d", "tz")).   -  person lukeA    schedule 09.05.2016
comment
Vectorize вашей функции, как предложил @lukeA, вы также можете использовать mutate_each, чтобы упростить изменение нескольких столбцов: df %>% mutate_each(funs(getLocTime(., tz)), matches("d"))   -  person eipi10    schedule 09.05.2016
comment
@lukeA: Отлично, это работает! Спасибо. (Документация dplyr может быть более явной в отношении необходимости векторизации при применении функций изменения столбцов...)   -  person egnha    schedule 09.05.2016
comment
@eipi10: я не знал о mutate_each — спасибо! Страница справки для mutate_each, похоже, не упоминает matches. Какова точная форма аргументов matches? Может ли быть несколько совпадений, то есть будет ли matches("d1", "d2") эквивалентным совпадением?   -  person egnha    schedule 09.05.2016
comment
matches задокументирован как select, который является глаголом для выбора столбцов фрейма данных. matches позволяет выбирать столбцы с помощью регулярного выражения. В этом случае d будет соответствовать любому столбцу, имя которого содержит d. При необходимости отрегулируйте имена ваших реальных столбцов или вы можете предпочесть использовать одну из других вспомогательных функций, описанных в разделе select, для выбора столбцов фрейма данных.   -  person eipi10    schedule 09.05.2016
comment
@eipi10: (Простите за последний вопрос!: это не работает. :/) Но спасибо за объяснение и за то, что указали мне на нужную документацию!   -  person egnha    schedule 09.05.2016
comment
Да, с matches будет работать только допустимое регулярное выражение. Вы можете использовать matches("d[12]") (эквивалентно matches("d1|d2"), но короче), но только "d" будет работать, если только вам не нужно исключить другие столбцы, содержащие d и другие символы. Например, если вам нужны только столбцы, начинающиеся с d и заканчивающиеся цифрами от 1 до 5, вы можете сделать matches("^d.*[1-5]$").   -  person eipi10    schedule 09.05.2016


Ответы (1)


Как упоминал lukeA, проблема возникает из-за того, что getLocTime не векторизован. Итак, либо вы векторизуете функцию, как было предложено, либо выполняете свою функцию построчно:

 df %>% rowwise() %>% mutate(d1 = getLocTime(d1, tz), d2 = getLocTime(d2, tz))

что гарантирует, что getLocTime вызывается с одним числом, а не с вектором. Я оставляю вам решать, какой подход быстрее.

person thothal    schedule 09.05.2016
comment
Я не проверял тайминги, но, судя по прошлому опыту, rowwise работает довольно медленно. - person eipi10; 09.05.2016