Preventing argument use in R

September 12, 2010
By

(This article was first published on 4D Pie Charts » R, and kindly contributed to R-bloggers)

It sounds silly, but sometimes you don’t want to let people use some arguments of a function. The canonical example is write.csv. The function is effectively a wrapper to write.table, but using “,” as the separator and “.” as the decimal.

We could implement the function in a really simple way, as

simple.write.csv <- function(...) write.table(sep = ",", dec = ".", ...)

Under most circumstances, this implementation works as expected. The problem occurs when we pass the sep or dec arguments into the function; in this case we get a rather unhelpful error message.

Error in write.table(sep = ",", dec = ".", ...) :
+++formal argument "sep" matched by multiple actual arguments

To get round this, the real implementation of write.csv is a little more complex. The basic idea behind it is this:

“Take a look at the call to the function write.csv. Warn the user if any forbidden arguments were passed in, then replace them with specified values. Then pass these on to write.table.”

Let’s take a look at the code.


write.csv <- function (...)
{
+++Call <- match.call(expand.dots = TRUE)
+++for (argname in c("append", "col.names", "sep", "dec", "qmethod"))
++++++if (!is.null(Call[[argname]]))
+++++++++warning(gettextf("attempt to set '%s' ignored", argname), domain = NA)
+++rn <- eval.parent(Call$row.names)
+++Call$append <- NULL
+++Call$col.names <- if (is.logical(rn) && !rn) TRUE else NA
+++Call$sep <- ","
+++Call$dec <- "."
+++Call$qmethod <- "double"
+++Call[[1L]] <- as.name("write.table")
+++eval.parent(Call)
}

Understanding this requires some technical knowledge of the R language. When you type things at the R command prompt and hit Enter, two things happen. Those characters are turned into a call, and then they are evaluated to give you your answer. Effectively, what happens is

eval(quote(the stuff you typed))

You can examine the contents of a call by converting it to be a list, for example


> as.list(quote(2+3))[[1]]
`+`

[[2]]
[1] 2

[[3]]
[1] 3

The first element of the list gives the function being called, and the rest of the elements are the arguments to pass in to that function. Notice that even ‘+’ is a function (and so is ‘<-’).

If you want to know more about this, then take a look at section 6.1 of the R Language definition. Expressions, which are, more or less, lists of calls are discussed in section 6.4.

match.call is like calling quote(the stuff you typed when you called this function)

Returning to the example, let’s have a sample call to write.csv.


dfr <- data.frame(x = 1:5, y = runif(5))
write.csv(dfr, file = "test.csv", sep = "!")

The crux of understanding write.csv is in the variable Call, assigned on the first line of the function.


> as.list(Call)[[1]]
write.csv

[[2]]
dfr

$file
[1] "test.csv"

$sep
[1] "!"

The rest of the function involves manipulating this object. The element sep is replaced with a comma (with a warning) and some additional arguments are set. The function that is called is then swapped for write.table, and finally this call is evaluated.

Deep understanding of this can get a bit mind-melting but if you want to use this principle in your own functions, you can more or less copy and paste from write.csv.


Tagged: calls, r

To leave a comment for the author, please follow the link and comment on his blog: 4D Pie Charts » R.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...



If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Tags: ,

Comments are closed.