Karl Johnson
Karl Johnson

Reputation: 111

How do you use tidydots(...) if a function you're writing also has optional parameters?

Is what I'm doing bad practice? I can't find anything specific to this topic, tidyeval docs don't mention it.

Writing a function that takes in a tibble, filtering parameters, grouping parameters and returns a summarized tibble.

createTable <- function(counts, mapper = NULL,day = NULL, week = NULL, month = NULL, ...){

  # Check for optional param
  day_missing <- is.null(day)
  print(day_missing)
  month_missing <- is.null(month)
  week_missing <- is.null(week)
  mapper_missing <- is.null(mapper)

  # reassigns input params to new vars for use with dplyr::filter
  m <- month
  d <- day
  w <- week
  mp <- mapper

  # group, summarise, filter and score counts
  group_vars <- enquos(...)
  counts %>% # counts is always in the same format, which allows for the below filtering
    group_by(!!!group_vars) %>%
    summarise_at(summaryVars, sum) %>% # summaryVars is a global var, always the same variables being summed
    filter(if(month_missing) TRUE else month == m) %>% 
    filter(if(week_missing) TRUE else week == w) %>% 
    filter(if(day_missing) TRUE else day == d) %>%
    filter(if(mapper_missing) TRUE else mapper == mp) %>% 
    calcScores() # calcScores is a global function, it converts the summarised data above into scored data (e.g. count of 10 = scored as 250)

}

This does exactly what I need it to do, but only if all parameters are supplied implicitly

test <- createTable(featureCounts2019, "mcglonee", NULL, NULL, 5, mapper, month, week)

#> Get expected output

test <- createTable(counts = featureCounts2019, mapper = "mcglonee", month = 5, mapper, month, week)

#> This won't work, R won't know what to do with the group_vars

I thought about supplying the group_vars as a list and then unlisting into enquos, but that throws me an error saying that objects in the list don't exist.

createTable2 <- function(counts, mapper = NULL,day = NULL, week = NULL, month = NULL, group_vars){

  # Check for optional param
  day_missing <- is.null(day)
  month_missing <- is.null(month)
  week_missing <- is.null(week)
  mapper_missing <- is.null(mapper)

  # reassigns input params to new vars for use with dplyr::filter
  m <- month
  d <- day
  w <- week
  mp <- mapper

  group_vars <- unlist(group_vars)
  # group, summarise, filter and score counts
  group_vars <- enquos(group_vars)
  counts %>% # counts is always in the same format, which allows for the below filtering
    group_by(!!!group_vars) %>%
    summarise_at(summaryVars, sum) %>% # summaryVars is a global var, always the same variables being summed
    filter(if(month_missing) TRUE else month == m) %>% 
    filter(if(week_missing) TRUE else week == w) %>% 
    filter(if(day_missing) TRUE else day == d) %>%
    filter(if(mapper_missing) TRUE else mapper == mp) %>% 
    calcScores() # calcScores is a global function, it converts the summarised data above into scored data (e.g. count of 10 = scored as 250)

}

test2 <- createTable2(counts = featureCounts2019, mapper = "mcglonee", month = 5, group_vars = list(mapper, month, week))

#> Error in unlist(group_vars) : object 'mapper' not found

Not the end of the world since this funciton is just for me, but what if I am writing something as part of a package I think people will use, what then? How are you able to have both optional parameters and use tidydots?

Upvotes: 3

Views: 110

Answers (1)

MorganK95
MorganK95

Reputation: 167

Your example is a little convoluted, but I think the Tidyverse design principles suggest a simple solution: place dots between your data argument and detail arguments like so:

   createTable <- function(counts, ..., mapper = NULL, 
                           day = NULL, week = NULL, month = NULL) { 

                   # function body
              }

As per design principles:

this forces the user of your function to fully name the detail arguments, because arguments that come after ... are never matched by position or partially by name

Additionally, you can check explicitly for missing function arguments with missing().

Upvotes: 1

Related Questions