twieg
twieg

Reputation: 73

Optional parameters in R function

I'm writing a function in R that takes either a data.frame or a file path, leading to a csv that would be read in as a data.frame.

I currently have this:

occInput <- function(fileInput, dfInput = NA) {  
  if (is.na(dfInput)) occ.dat <- read.csv(file = fileInput)
  else occ.dat <- dfInput

  #manipulate the data in occ.dat
}

However, when I actually pass in a data.frame as the dfInput parameter, it throws the warning:

Warning in if (is.na(dfInput)) occ.dat <- read.csv(file = fileInput) else occ.dat <- dfInput: the condition has length > 1 and only the first element will be used

While this does not actually adversely affect my code, it's ugly, and it suggests to me that there is a more elegant way to have either/or optional arguments in a function.

Any suggestions? I feel like I'm overlooking something obvious.

Upvotes: 0

Views: 7142

Answers (4)

Spacedman
Spacedman

Reputation: 94182

Why have two arguments? Just have one and test if its data-framey:

occInput <- function(Input) {  
  if (!inherits(Input,"data.frame")){
    # its not a data frame, so treat as a file name...
    Input = read.csv(Input)
  }
  # Now Input is a data frame...
}

works with data frames, data tables, and tibbles.

Upvotes: 1

MKR
MKR

Reputation: 20095

You should not trust on NA when argument is expected to get value as data.frame, list etc. is.na returns NA check for each element to those data structures. The better option could be to initialize your argument with NULL and check with is.NULL. Another option could be to use missing function on dfInput argument. Your function definition can be written as:

occInput <- function(fileInput, dfInput = NULL) {  
  if (is.NULL(dfInput)) occ.dat <- read.csv(file = fileInput)
  else occ.dat <- dfInput

  #manipulate the data in occ.dat
}

#OR you can just use missing function

occInput <- function(fileInput, dfInput) {  
  #Perform missing check
  if (missing(dfInput)) occ.dat <- read.csv(file = fileInput)
  else occ.dat <- dfInput

  #manipulate the data in occ.dat
}

Upvotes: 6

R Yoda
R Yoda

Reputation: 8760

if does only check the first element of a vector otherwise you get mentioned warning.

Checking the class of the passed argument is quite more error tolerant:

occInput <- function(fileInput, dfInput = NA) {  
  if ("data.frame" %in% class(dfInput)) print("dfInput contains a data.frame") # read.csv(file = fileInput)
}

> occInput("adsf")
> occInput("adsf", "asdf")
> occInput("adsf", NULL)
> occInput("adsf", 1:10)
> occInput(dfInput = as.data.frame(mtcars))
[1] "dfInput contains a data.frame"

Upvotes: 0

twieg
twieg

Reputation: 73

I've found one (slightly hacky) way to do it: use if (identical(dfInput, NA)) rather than if (is.na(dfInput)), which does not throw any errors, as it checks whether the two arguments are identical rather than equivalent.

However, it still doesn't seem very elegant to me. I'll leave the question unsolved in case someone else wants to take a better stab at it.

Upvotes: 0

Related Questions