Reputation: 73
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
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
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
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
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