Many of R's builtin functions have been implemented to accept a variable number of parameters. This is represented by ...
, and is usually (but not always) the last item in a function's call signature. The signature for lapply is a good example, and is given by the following:
lapply(X, FUN, ...)
Within the context of lapply, ...
serves as a placeholder for additional arguments that should be passed to FUN (as long as they are constant, and do not vary along with X).
We can take advantage of variable parameterization in user-defined functions. This post demonstrates how to write functions that accept a variable number of arguments, along with a possible use case the leverages the openxlsx library. To follow along with the examples, you should have a relatively recent installation of R, in addition to the openxlsx library.
Implementing Variable Argument Functions in R
Our goal will be to create a function that takes a filepath and a variable number of data.frames, and will write each data.frame to a separate worksheet in a common Excel workbook. Here's a look at the function's header:
xlsxCompiler = function(xlsxPath, ...)
Where xlsxPath represents the filesystem location to which the workbook should be written, and ...
representing the variable number of data.frames that can be passed into xlsxCompiler.
Within the context of an executing function in R, the expression as.list(match.call(expand.dots=TRUE))
returns a list containing the name of the function as a string in the first position, any keyword parameters and values in the next position(s), followed by any other variable/optional parameter names. Using the builtin datasets trees, women and rock, we demonstrate what calling as.list(match.call(expand.dots=TRUE))
returns:
xlsxCompiler = function(xlsxPath, ...) {
allFuncArgs = as.list(match.call(expand.dots=TRUE))
return(allFuncArgs)
}
> xlsxCompiler(xlsxPath="example.xlsx", trees, women, rock)
[[1]]
xlsxCompiler
$xlsxPath
[1] "E:\\Data\\example.xlsx"
[[3]]
trees
[[4]]
women
[[5]]
rock
This output matches exactly with what was described above. One thing to note is that elements 3-5 are the names of the objects passed as optional parameters, not the objects themselves. In order to capture the objects (data.frames) that correspond to the optional parameters, we enclose ...
in list, and assign the result to a local variable within xlsxCompiler. To demonstrate, we'll print out the first 5 rows of each data.frame passed into our function:
xlsxCompiler = function(xlsxPath, ...) {
allFuncArgs = as.list(match.call(expand.dots=TRUE))
varObjArgs = list(...)
return(lapply(varObjArgs, head, n=5))
}
> xlsxCompiler(xlsxPath="E:\\Data\\example.xlsx", trees, women, rock)
[[1]]
Girth Height Volume
1 8.3 70 10.3
2 8.6 65 10.3
3 8.8 63 10.2
4 10.5 72 16.4
5 10.7 81 18.8
[[2]]
height weight
1 58 115
2 59 117
3 60 120
4 61 123
5 62 126
[[3]]
area peri shape perm
1 4990 2791.90 0.0903296 6.3
2 7002 3892.60 0.1486220 6.3
3 7558 3930.66 0.1833120 6.3
4 7352 3869.32 0.1170630 6.3
5 7943 3948.54 0.1224170 17.1
The three data.frame's correspond to the first five records of the trees, women and rock datasets respectively. Since we have the names of the optional arguments contained within the allFuncArgs variable, and the objects themselves in varObjArgs, all that remains to be done is to write the data.frames to a workbook and save the file to the location specified by xlsxPath. Here's the full specification for xlsxCompiler, which incorporates functionality available in the openxlsx package:
require("openxlsx")
xlsxCompiler = function(xlsxPath, ...) {
varObjArgs = list(...)
allFuncArgs = as.list(match.call(expand.dots=TRUE))
optFuncNames = as.character(allFuncArgs[3:length(allFuncArgs)])
wb = openxlsx::createWorkbook()
for (ii in 1:length(varObjArgs)) {
iterName = optFuncNames[[ii]]
iterDF = varObjArgs[[ii]]
if (is.data.frame(iterDF)) {
openxlsx::addWorksheet(wb,iterName)
openxlsx::writeData(
wb,iterName,iterDF,xy=c(1, 1),colNames=TRUE,
rowNames=FALSE
)
}
}
openxlsx::saveWorkbook(wb, file=xlsxPath, overwrite=TRUE)
}
# Then, call xlsxCompiler without assignment.
> xlsxCompiler(xlsxPath="example.xlsx", trees, women, rock)
xlsxCompiler doesn't return a value or object. Upon successful execution, a workbook named in accordance with xlsxPath will have been created, containing the data.frames passed into xlsxCompiler as optional arguments.