0

I am trying to write a function that sets the default for some arguments to NULL. However, when I then want to specify those arguments to not be NULL when calling the function, I receive the error that those objects cannot be found.

Simple starter function:

library(dplyr)
toydf <- data.frame(Name = "Fred", Age="Old", Hair="Brown")
toyA <- function(df, groupA){
  data.frame(A = df%>%dplyr::select({{groupA}}))
}
toyA(toydf, Name)
toyA(toydf, Age)

I want to specify some arguments to have a NULL default, which this and this seem to suggest should work. I try this:

toyB <- function(df, groupA, groupB=NULL, groupC=NULL){
  if((!is.null(groupB)) & (!is.null(groupC))){
    data.frame(A = df%>%dplyr::select({{groupA}}),
               B = df%>%dplyr::select({{groupB}}),
               C = df%>%dplyr::select({{groupC}}))
  }
  else{
    data.frame(A = df%>%dplyr::select({{groupA}}))
  }
}

But this throws me the error:

toyB(toydf, Name, Age, Hair)
Error in toyB(toydf, Name, Age, Hair) : object 'Age' not found

We can work around it by checking missing() as some other questions and solutions suggest.

toyC <- function(df, groupA, groupB, groupC){
  if((!missing(groupB)) & (!missing(groupC))){
    data.frame(A = df%>%dplyr::select({{groupA}}),
               B = df%>%dplyr::select({{groupB}}),
               C = df%>%dplyr::select({{groupC}}))
  }
  else{
    data.frame(A = df%>%dplyr::select({{groupA}}))
  }
}
toyC(toydf,Name)
toyC(toydf, Name, Age, Hair)

Why is it that the NULL default is not working?

2
  • 1
    Haven't opened R to test so this is a comment, but pretty sure it's a non-standard evaluation thing. You {{}} the arguments inside select but can't when checking is.null, so R tries to check is.null(groupB), which resolves to is.null(Age), and then can't find an object Age to check (because it's a column of the dataframe). Commented Oct 10, 2023 at 18:21
  • 1
    Possible duplicate of stackoverflow.com/questions/68007842/…. When you check is.null(GroupB) you are evaluating the promise that points to Age and that variable doesn't exist at the time you are checking is.null, it only exists in the data.frame. The dup shows you how to avoid the early evaluation. Commented Oct 10, 2023 at 19:14

1 Answer 1

1

You should directly use the NSE within select.

toyB <- function(df, groupA, groupB=NULL, groupC=NULL){
  df %>% select({{groupA}}, {{groupB}}, {{groupC}})
}

toyB(head(iris), Species)
  Species
1  setosa
2  setosa
3  setosa
4  setosa
5  setosa
6  setosa

toyB(head(iris), Species, Sepal.Length, Sepal.Width)
  Species Sepal.Length Sepal.Width
1  setosa          5.1         3.5
2  setosa          4.9         3.0
3  setosa          4.7         3.2
4  setosa          4.6         3.1
5  setosa          5.0         3.6
6  setosa          5.4         3.9
Sign up to request clarification or add additional context in comments.

5 Comments

I would imagine the OP is doing more with these variables, but if all they were doing was simply selecting then they could do: toyB <- function(df, ...) df %>% select(...)
toyB <- \(df, ...) df[sapply(substitute(...()), deparse)] might be more efficient.
@jay.sf It all depends as to whether OP is interested in using the variable names again. If not needed then even toyB <- \(df, ...) df[as.character(substitute(...()))] should work
@Onyambu Even more efficient, true.
My post was not clear as to my real needs. I was primarily trying to figure out how to properly check if the arguments were null. It was not about actually selecting the columns. I believe MrFlick's linked question under my post is what I need.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.