R dplyr:将函数用作下一列中的一列中的字符串

R dplyr: Use the function as a string in one column on the next column

我想使用 dplyr 将名称作为字符串存储在一列中的函数应用于另一列中的值。 我已经使用 mutate_.dots 参数尝试了几件事,但我现在卡住了。

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

我们可以使用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

注意:无需额外努力创建查找表

您可以尝试使用 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))))

我能想到的一种方法是使用创建查找 table,然后使用 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

要在此处进行完整的 tidyverse,我们可以使用 purrr 的 invoke_map() 函数。它需要一个函数列表和一个参数值列表列表以用于每个函数。它就像一个向量化的 do.call()

df$fun 中的 lubridate 函数需要一个参数 x,因此我们需要创建一个列表列表,每个日期存储为一个名为 x 的元素。我们可以通过复制日期列并使用 nest() 创建 list-column of data-frames。

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 中的每个元素都是一个 data-frame 和列 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

但是因为我们知道这些函数 return 每个只有一个数值,所以我们可以用 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