3
votes

How does R dispatch plot functions? The standard generic is defined as

plot <- function (x, y, ...)  UseMethod("plot")

So usually, all plot methods need arguments x and y. Yet, there exists a variety of other functions with different arguments. Some functions only have argument x:

plot.data.frame <- function (x, ...)

others even have neither x nor y:

plot.formula <- function(formula, data = parent.frame(), ..., subset,
                         ylab = varnames[response], ask = dev.interactive()) 

How does this work and where is this documented?


Background

In my package papeR (see GitHub) I want to replace the function plot.data.frame, which is defined in the R package graphics with my own version. Yet, this is usually not allowed

Do not replace registered S3 methods from base/recommended packages, something which is not allowed by the CRAN policies and will mean that everyone gets your method even if your namespace is unloaded.

as Brian Ripley let me know last time I tried to do such a thing. A possible solution is as follows:

If you want to change the behaviour of a generic, say predict(), for an existing class or two, you could add such as generic in your own package with default method stats::predict, and then register modified methods for your generic (in your own package).

For other methods I could easily implement this (e.g. toLatex), yet, with plot I am having problems. I added the following to my code:

## overwrite standard generic
plot <- function(x, y, ...)
    UseMethod("plot")

## per default fall back to standard generic
plot.default <- function(x, y, ...)
    graphics::plot(x, y, ...)

## now specify modified plot function for data frames
plot.data.frame <- function(x, variables = names(x), ...)

This works for data frames and plots with x and y. Yet, it does not work if I try to plot a formula etc:

Error in eval(expr, envir, enclos) : 
  argument "y" is missing, with no default

I also tried to use

plot.default <- function(x, y, ...) 
    UseMethod("graphics::plot")

but then I get

Error in UseMethod("graphics::plot") : 
  no applicable method for 'graphics::plot' applied to an object of class "formula"

So the follow up question is how I can fix this?


[Edit:] Using my solution below fixes the problems within the package. Yet, plot.formula is broken afterwards:

library("devtools")
install_github("hofnerb/papeR")
example(plot.formula, package="graphics") ## still works

library("papeR")
example(plot, package = "papeR") ## works
### BUT
example(plot.formula, package="graphics") ## is broken now
1
Why do you override plot and plot.default? Just using plot.data.frame <- function(...) {print("Cheese")}; plot(data.frame(1)) gives me the "expeced behavior". Anyway, interesting question … - CL.
plot.formula has got a mandatory second argument (the data). That it isn't called y is irrelevant since argument matching is done by position. Consider this: plot.myclass <- function(a, b, ...) print(b); x <- 1; y <- 2; class(x) <- "myclass"; plot(x, y) However, I beleive the generic can be called with less than two arguments because of the special evaluation done by UseMethod. See the language definition section 5.4. - Roland
@user2706569: As far as I understand Brian Ripley correctly, this is prohibited. See also issue #5. - Benjamin Hofner
@Roland: Ok. So if argument matching is done by position only, why do I get the observed errors (or even better get rid of them)? The second error (no applicable method for 'graphics::plot') looks like I cannot find plot.formula as it isn't exported? - Benjamin Hofner
It is not done by position only. It follows the standard rules of argument matching (first name, position if no match). The first parameter of UseMethod must name a function and graphics::plot is not a function name. Rather, it is a call (to function ::). Doesn't plot.default <- function(...) graphics::plot(...) work for you? (I don't want to build a package for testing right now.) - Roland

1 Answers

4
votes

Thanks to @Roland I solved part of my problem.

It seems that the position of the arguments are used for method dispatch (and not only the names). Names are however partially used. So with Rolands example

> plot.myclass <- function(a, b, ...) 
>    print(b) 
> x <- 1
> y <- 2
> class(x) <- "myclass"

we have

> plot(x, y)
[1] 2
> plot(a = x, b = y)
[1] 2

but if we use the standard argument names

> plot(x = x, y = y)
Error in print(b) (from #1) : argument "b" is missing, with no default

it doesnt't work. As one can see x is correctly used for the dispatch but b is then "missing". Also we cannot swap a and b:

> plot(b = y, a = x)
Error in plot.default(b = y, a = x) : 
  argument 2 matches multiple formal arguments

Yet, one could use a different order if the argument one wants to dispatch for is the first (?) element without name:

> plot(b = y, x)
[1] 2

Solution to the real problem:

I had to use

plot.default <- function(x, y, ...)
    graphics::plot(x, y, ...)

The real issue was internally in my plot.data.frame method where I was using something along the lines of:

plot(x1 ~ y1)

without specifying data. Usually this works as data = parent.frame() per default. Somehow in this case this wasn't working. I now use plot(y1, x1) which works like a charm. So this was my sloppiness.