Cannot make mutate_at to work with ends_with()

I have the following dataframe:

dframe <- structure(list(y = c(-0.551803287760631, -1.30494019324738, 0.00821236626893252, 
0.638916511414093, -0.816805971651003, 1.12037288852287), Reg_Date = structure(c(1420217760, 
1420217760, 1420217880, 1420217880, 1420217880, 1420217880), class = c("POSIXct", 
"POSIXt"), tzone = "UTC"), Del_Date = structure(c(NA, NA, 1468065900, 
1468065900, 1468065900, 1468065900), class = c("POSIXct", "POSIXt"
), tzone = "UTC"), days = c(1042L, 1042L, 554L, 554L, 554L, 554L
), Start_Date = structure(c(1420217880, 1420217880, 1420218180, 
1420218180, 1420218180, 1420218180), class = c("POSIXct", "POSIXt"
), tzone = "UTC"), Stop_Date = structure(c(NA, NA, 1468065900, 
1468065900, 1468065900, 1468065900), class = c("POSIXct", "POSIXt"
), tzone = "UTC"), group = c("A", "B", "C", "D", "E", "F")), row.names = c(NA, 
-6L), class = c("tbl_df", "tbl", "data.frame"), .Names = c("y", 
"Reg_Date", "Del_Date", "days", "Start_Date", "Stop_Date", "group"
))

I would like to change the format of all dates, and I try

> dframe %>% mutate_at(ends_with("Date"), format, usetz = TRUE)
Error: Variable context not set

What does this error mean? According to ?mutate_at,

summarise_at(), mutate_at() and transmute_at() allow you to select columns using the same name-based select_helpers just like with select().

The problem is not due to format, because using another function I get the same error:

> dframe %>% mutate_at(ends_with("Date"), as.character)
Error: Variable context not set

Can you help me? In my real use case I have much more Date columns (and even more non-Date columns), so having to select them by name is a real hassle. Sure, I could grep in names(dframe), but, unless you think that base R is more appropriate in this case, I'd rather see a simple, readable dplyr solution. Thanks!

You have to be explicit and include the variable selection within the vars() argument of mutate_at() if you are using a helper like ends_with(). Does this get you what you are looking for?

dframe %>% mutate_at(vars(ends_with("Date")), funs(format(., usetz = TRUE)))
#> # A tibble: 6 x 7
#>              y                Reg_Date                Del_Date  days
#>          <dbl>                   <chr>                   <chr> <int>
#> 1 -0.551803288 2015-01-02 16:56:00 UTC                    <NA>  1042
#> 2 -1.304940193 2015-01-02 16:56:00 UTC                    <NA>  1042
#> 3  0.008212366 2015-01-02 16:58:00 UTC 2016-07-09 12:05:00 UTC   554
#> 4  0.638916511 2015-01-02 16:58:00 UTC 2016-07-09 12:05:00 UTC   554
#> 5 -0.816805972 2015-01-02 16:58:00 UTC 2016-07-09 12:05:00 UTC   554
#> 6  1.120372889 2015-01-02 16:58:00 UTC 2016-07-09 12:05:00 UTC   554
#> # ... with 3 more variables: Start_Date <chr>, Stop_Date <chr>,
#> #   group <chr>

Also, you could look into using the lubridate package which makes working with dates a lot easier:

library(lubridate)
dframe %>% mutate_at(vars(ends_with("Date")), ymd_hms)
#> # A tibble: 6 x 7
#>              y            Reg_Date            Del_Date  days          Start_Date
#>          <dbl>              <dttm>              <dttm> <int>              <dttm>
#> 1 -0.551803288 2015-01-02 16:56:00                  NA  1042 2015-01-02 16:58:00
#> 2 -1.304940193 2015-01-02 16:56:00                  NA  1042 2015-01-02 16:58:00
#> 3  0.008212366 2015-01-02 16:58:00 2016-07-09 12:05:00   554 2015-01-02 17:03:00
#> 4  0.638916511 2015-01-02 16:58:00 2016-07-09 12:05:00   554 2015-01-02 17:03:00
#> 5 -0.816805972 2015-01-02 16:58:00 2016-07-09 12:05:00   554 2015-01-02 17:03:00
#> 6  1.120372889 2015-01-02 16:58:00 2016-07-09 12:05:00   554 2015-01-02 17:03:00
#> # ... with 2 more variables: Stop_Date <dttm>, group <chr>
3 Likes

This looks cool! I'm traveling and I can't test it right now, but I'll check this evening and let you know. Thanks!

Ps you say that I have to be explicit if I want to use helper functions with mutate_at. Do you mean that there's a simpler way to mutate all columns whose name ends in Date? This seems simple to me, but of course if there's a simpler and/or more idiomatic dplyr form, I'd like to know.

Assumming all of your data columns are class POSIX, you could try something like this:

library(lubridate)
library(dplyr)
df <- dframe %>%
  mutate_if(is.POSIXt, as_datetime)

glimpse(df)

@Andrea Is this what you are trying to accomplish?

1 Like

@pgensler your solution also works, but I already accepted @mfherman solution. Hope that's ok! Very nice solution, anyway

Glad this works for you! When I say explicit, I just mean you have to put the select helper within the vars() helper function so that mutate_at knows to quote and evaluate in the correct context. Here's a little more info on the vars() function.

1 Like