R dplyr:在下一列

时间:2017-02-01 09:21:27

标签: r dplyr lubridate

我想使用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

4 个答案:

答案 0 :(得分: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 :(得分: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

注意:不需要额外的努力来创建查找表

答案 2 :(得分: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))))

答案 3 :(得分:1)

要在这里完全整理,我们可以使用purrr的invoke_map()功能。它需要一个函数列表和一个用于每个函数的参数值列表。它就像一个矢量化的do.call()

df$fun中的lubridate函数期望参数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