2017-02-01 3 views
2

Я хочу применить функцию, имя которой хранится в столбце в виде строки, в значении в другом столбце, используя dplyr. Я пробовал несколько вещей, используя mutate_ и аргумент .dots, но сейчас я застрял.R dplyr: Используйте функцию как строку в одном столбце в следующем столбце

library(lubridate) 
library(dplyr) 

df <- data.frame(date=as.POSIXct('2017/01/01 12:34') + 1:10*123456, 
       fun=rep(c('minute','hour','day','month','year'),2)) 

вход:

> df 
        date fun 
1 2017-01-02 22:51:36 minute 
2 2017-01-04 09:09:12 hour 
3 2017-01-05 19:26:48 day 
4 2017-01-07 05:44:24 month 
5 2017-01-08 16:02:00 year 
6 2017-01-10 02:19:36 minute 
7 2017-01-11 12:37:12 hour 
8 2017-01-12 22:54:48 day 
9 2017-01-14 09:12:24 month 
10 2017-01-15 19:30:00 year 

выход:

    date fun res 
1 2017-01-02 22:51:36 minute 51 
2 2017-01-04 09:09:12 hour 9 
3 2017-01-05 19:26:48 day 5 
4 2017-01-07 05:44:24 month 1 
5 2017-01-08 16:02:00 year 2017 
6 2017-01-10 02:19:36 minute 19 
7 2017-01-11 12:37:12 hour 12 
8 2017-01-12 22:54:48 day 12 
9 2017-01-14 09:12:24 month 1 
10 2017-01-15 19:30:00 year 2017 

ответ

2

Вы можете попробовать, что с do.call, но вы должны использовать rowwise:

library("dplyr") 
library("lubridate") 

df <- data.frame(
    date = as.POSIXct('2017/01/01 12:34') + 1:10*123456, 
    fun = rep(c('minute','hour','day','month','year'),2), 
    stringsAsFactors = FALSE 
) 

df %>% rowwise() %>% mutate(res = as.character(do.call(fun, list(date)))) 
+0

Хотя мне нравятся все остальные ответы, этот пример ближе всего к тому, что я изначально думал. – Wietze314

2

Мы можем использовать mapply

df$res <- mapply(function(x,y) get(x)(y), as.character(df$fun), df$date) 
df$res 
#[1] 51 9 5 1 2017 19 12 12 1 2017 

Другой вариант data.table

library(data.table) 
setDT(df)[, res := as.integer(get(as.character(fun))(date)), 1:nrow(df)] 
df 
#     date fun res 
#1: 2017-01-02 22:51:36 minute 51 
#2: 2017-01-04 09:09:12 hour 9 
#3: 2017-01-05 19:26:48 day 5 
#4: 2017-01-07 05:44:24 month 1 
#5: 2017-01-08 16:02:00 year 2017 
#6: 2017-01-10 02:19:36 minute 19 
#7: 2017-01-11 12:37:12 hour 12 
#8: 2017-01-12 22:54:48 day 12 
#9: 2017-01-14 09:12:24 month 1 
#10: 2017-01-15 19:30:00 year 2017 

ПРИМЕЧАНИЕ: Не делая никаких дополнительных усилий в создании справочных таблиц

+0

Есть ли способ достичь этого в цепочке 'dplyr'? – Wietze314

+0

@ Wietze314 Вы можете использовать 'map' из' purrr', который также является частью 'tidyverse' – akrun

+0

Благодарим вас за ответ. Мне интересно узнать больше о purrr (и data.table, который я откладываю все время). – Wietze314

5

Один из способов , Я мог бы подумать, что это создание таблицы поиска, а затем получение правильного формата вывода с использованием match

x <- c("minute", "hour", "day", "month", "year") 
y <- c("%M", "%H", "%d", "%m", "%Y") 

format(df$date, format = y[match(df$fun, x)]) 
#[1] "51" "09" "05" "01" "2017" "19" "12" "12" "01" "2017" 

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

Если нам это нужно в dplyr цепи

library(dplyr) 
df %>% 
    mutate(res = format(date, format = y[match(df$fun, x)])) 


#     date fun res 
#1 2017-01-02 22:51:36 minute 51 
#2 2017-01-04 09:09:12 hour 09 
#3 2017-01-05 19:26:48 day 05 
#4 2017-01-07 05:44:24 month 01 
#5 2017-01-08 16:02:00 year 2017 
#6 2017-01-10 02:19:36 minute 19 
#7 2017-01-11 12:37:12 hour 12 
#8 2017-01-12 22:54:48 day 12 
#9 2017-01-14 09:12:24 month 01 
#10 2017-01-15 19:30:00 year 2017 
+1

Мне это нравится. Векторизация ftw. –

+1

Спасибо за ответ. Это также очень элегантное решение – Wietze314

1

идти полным tidyverse здесь, мы можем использовать функцию invoke_map() purrr в. Он принимает список функций и список списков значений параметров для каждой функции. Это как векторизованный do.call().

Функции lubridate в df$fun ожидают аргумента x, поэтому нам нужно создать список списков с каждой датой, хранящейся как элемент с именем x. Мы можем создать столбец-столбец данных-кадров, скопировав столбец даты и используя nest().

df2 <- df %>% 
    mutate(x = date) %>% 
    tidyr::nest(x, .key = "params") 
df2 
#> # A tibble: 10 × 3 
#>     date fun   params 
#>     <dttm> <chr>   <list> 
#> 1 2017-01-02 22:51:36 minute <tibble [1 × 1]> 
#> 2 2017-01-04 09:09:12 hour <tibble [1 × 1]> 
#> 3 2017-01-05 19:26:48 day <tibble [1 × 1]> 
#> 4 2017-01-07 05:44:24 month <tibble [1 × 1]> 
#> 5 2017-01-08 16:02:00 year <tibble [1 × 1]> 
#> 6 2017-01-10 02:19:36 minute <tibble [1 × 1]> 
#> 7 2017-01-11 12:37:12 hour <tibble [1 × 1]> 
#> 8 2017-01-12 22:54:48 day <tibble [1 × 1]> 
#> 9 2017-01-14 09:12:24 month <tibble [1 × 1]> 
#> 10 2017-01-15 19:30:00 year <tibble [1 × 1]> 

Каждый элемент в столбце params представляет собой данные кадра с колонной x. Это наш список списков.

df2$params[1] 
#> [[1]] 
#> # A tibble: 1 × 1 
#>      x 
#>     <dttm> 
#> 1 2017-01-02 22:51:36 

С нашими двумя списками, мы можем использовать invoke_map() и получить список результатов.

str(purrr::invoke_map(df2$fun, df2$params)) 
#> List of 10 
#> $ : int 51 
#> $ : int 9 
#> $ : int 5 
#> $ : num 1 
#> $ : num 2017 
#> $ : int 19 
#> $ : int 12 
#> $ : int 12 
#> $ : num 1 
#> $ : num 2017 

Но поскольку мы знаем, что эти функции возвращают только один числовое значение каждого, мы можем получить результаты в хорошем векторе с invoke_map_dbl().

df2 %>% 
    mutate(res = purrr::invoke_map_dbl(fun, params)) %>% 
    select(-params) 
#> # A tibble: 10 × 3 
#>     date fun res 
#>     <dttm> <chr> <dbl> 
#> 1 2017-01-02 22:51:36 minute 51 
#> 2 2017-01-04 09:09:12 hour  9 
#> 3 2017-01-05 19:26:48 day  5 
#> 4 2017-01-07 05:44:24 month  1 
#> 5 2017-01-08 16:02:00 year 2017 
#> 6 2017-01-10 02:19:36 minute 19 
#> 7 2017-01-11 12:37:12 hour 12 
#> 8 2017-01-12 22:54:48 day 12 
#> 9 2017-01-14 09:12:24 month  1 
#> 10 2017-01-15 19:30:00 year 2017 
Смежные вопросы