xhr489
xhr489

Reputation: 2309

Tidyeval in own functions inside own functions with the pipe

So I am trying to make a package (I have not included my roxygen2 headers below):

I have this function:

date_from_text <- function(df, x){
  x <- rlang::enquo(x)
  name <- rlang::quo_name(x)

  df %>%
    dplyr::mutate(!!name := lubridate::ymd_hms(!!x))
}

And when the datetime column has the right class then I use this to extract all the component.

date_columns <- function(df, x){

  x <- rlang::enquo(x)

  df %>%
      dplyr::mutate(year=lubridate::year(!!x),
           ydag=lubridate::yday(!!x),
           weekday=lubridate::wday(!!x, label=FALSE),
           hour = lubridate::hour(!!x),
           hour_min= hms::as.hms(lubridate::force_tz(!!x)),
           week_num = lubridate::week(!!x),
           month = lubridate::month(!!x),
           date = lubridate::date(!!x))
}

I don't wnat the date_from_text function to be included in the NAMESPACE and I want to include it somehow in the date_columns function. Something like checking if the timestamp has the right class and if not then change the class.. and then create all the datetime components.

I don't know how to call the first function inside the other function.

data for testing :

df <- structure(list(time = c("2018-01-30 20:08:18", "2018-02-01 21:01:25", 
"2018-01-31 23:25:12", "2018-01-28 23:45:34", "2018-01-31 12:00:55", 
"2018-02-04 09:15:31", "2018-01-27 21:08:02", "2018-02-08 01:50:31", 
"2018-02-01 03:15:43", "2018-02-04 01:04:52"), type = c("A", 
"D", "B", "B", "B", "D", "C", "C", "C", "A")), .Names = c("time", 
"type"), row.names = c(NA, -10L), class = c("tbl_df", "tbl", 
"data.frame"))

UPDATE: So I have now included date_from_text into the date_columns

date_columns <- function(df, x){
  x <- rlang::enquo(x)

  out <-  df %>%
    date_from_text(!!x) %>%
      dplyr::mutate(year=lubridate::year(!!x),
           ydag=lubridate::yday(!!x),
           weekday=lubridate::wday(!!x, label=FALSE),
           hour = lubridate::hour(!!x),
           hour_min= hms::as.hms(lubridate::force_tz(!!x)),
           week_num = lubridate::week(!!x),
           month = lubridate::month(!!x),
           date = lubridate::date(!!x))
 out

  }

So I don't understand why I have to use !!x again inside date_columns?? It is already included inside date_from_text. I am calling the function not creating it...

Upvotes: 3

Views: 223

Answers (1)

hplieninger
hplieninger

Reputation: 3494

As worked out in comments and chat, the question is neither about package development and namespaces nor about the pipe. The question is how to use in possibly nested wrapper functions.

The answer is that expressions as passed by the user to the function need to be quoted and unquoted, just as in date_from_text() below via enquo(x) and !!x.

date_from_text <- function(df, x) {
        x <- rlang::enquo(x)                                  # quote via enquo()
        name <- rlang::quo_name(x) 
        dplyr::mutate(df, !!name := lubridate::ymd_hms(!!x))  # unquote via !!
}

Then, expressions can be passed to such functions in "interactive" mode, just like dplyr functions:

date_from_text(df, time)
select(df, time)

Remember that functions inside functions don't take expressions, but expressions that are quoted and unquoted, just like in the mutate() call with enquo(x) and !!x above.

This means, that, in date_from_text() below, both the call date_from_text() and mutate() need to receive !!x.

date_columns <- function(df, x) {
        x <- rlang::enquo(x)

        out <- date_from_text(df, !!x)
        out <- dplyr::mutate(out, year = lubridate::year(!!x))
        return(out)
}

Apart from that, in package development, you can use all of your functions whether they are exported or not (as I did with date_from_text() inside date_columns()). Exported functions need to be documented and can be used after installation via library(pkg) or pkg::fun(), whereas unexported functions can only be used after installation via pkg:::fun().

I modified @David's functions in order to focus on the relevant parts.

Upvotes: 1

Related Questions