Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method dispatch for generic `plot` function in R

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
like image 277
Benjamin Hofner Avatar asked Mar 03 '26 00:03

Benjamin Hofner


1 Answers

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.

like image 141
Benjamin Hofner Avatar answered Mar 05 '26 13:03

Benjamin Hofner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!