Mutate_at to date across multiple tibbles

dplyr
purrr

#1

I posted this question on Stack Overflow, which was answered successfully. But I then realised that my real-life case is considerably more complicated, and so am asking for some additional tidyverse-specific assistance.

I have a number of tibbles, each with several variables that I would like to convert to date class. Some variables are common across tables; others are not.

The linked example didn't work in real life because the lists are of different lengths.

library(purrr)
library(tibble)
library(lubridate)

df1 <- tribble(~date_a, ~ date_b,     ~value_a,
               "2017-1-3", "2013-7-9", 10,
               "2018-2-7", "2017-6-2", 13,
               "2018-5-7", "2014-8-9", 35)

df2 <- tribble(~date_b,   ~date_c,    ~value_b,
               "2014-1-7", "2018_4_5", 10,
               "2018-4-9",  "2016-4-3", 6,
               "2018-5-8", "2012-5-2", 18)

list_dfs <- list('df1' = df1, 
                 'df2' = df2)
list_dates <- c("date_a", "date_b", "date_c", "date_d")

out <- map2(list_dfs, list_dates, ~ mutate_at(.x, c(.y), ymd))
#> Error: `.x` (2) and `.y` (4) are different lengths

What I want is, for each tibble within list_df, convert any variables that appear in list_dates to date class. Would be ideal if there was a way of doing this with no intermediary steps (i.e. mutating in place across all the tibbles).

Searching, it seems like cross2 might be a solution, but I guess the mutate_at call will fail if a date variable doesn't appear in the tibble.

Grateful for any suggestions.


#2

If you are OK with warnings, then you can do it this way:

library(purrr)
library(tibble)
library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following object is masked from 'package:base':
#> 
#>     date
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:lubridate':
#> 
#>     intersect, setdiff, union
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

df1 <- tribble(~date_a, ~ date_b,     ~value_a,
               "2017-1-3", "2013-7-9", 10,
               "2018-2-7", "2017-6-2", 13,
               "2018-5-7", "2014-8-9", 35)

df2 <- tribble(~date_b,   ~date_c,    ~value_b,
               "2014-1-7", "2018_4_5", 10,
               "2018-4-9",  "2016-4-3", 6,
               "2018-5-8", "2012-5-2", 18)

list_dfs <- list('df1' = df1, 
                 'df2' = df2)
list_dates <- c("date_a", "date_b", "date_c", "date_d")

out <- map(list_dfs, ~ mutate_at(.x, vars(one_of(list_dates)), ymd))
#> Warning: Unknown variables: `date_c`, `date_d`
#> Warning: Unknown variables: `date_a`, `date_d`
out
#> $df1
#> # A tibble: 3 x 3
#>   date_a     date_b     value_a
#>   <date>     <date>       <dbl>
#> 1 2017-01-03 2013-07-09    10.0
#> 2 2018-02-07 2017-06-02    13.0
#> 3 2018-05-07 2014-08-09    35.0
#> 
#> $df2
#> # A tibble: 3 x 3
#>   date_b     date_c     value_b
#>   <date>     <date>       <dbl>
#> 1 2014-01-07 2018-04-05   10.0 
#> 2 2018-04-09 2016-04-03    6.00
#> 3 2018-05-08 2012-05-02   18.0

Created on 2018-05-22 by the reprex package (v0.2.0).


#3

This works wonderfully - thanks! Great use of the one_of helper function.