Skip to content

Make add_ggplot an S3 method #3815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

schloerke
Copy link
Contributor

  • Situation:
    • GGally has ggplot2-like object "ggmatrix". I would like to be able to add themes and labels.
  • Issue:
    • I currently do this by overwriting +.gg, which produces a warning.
library(ggplot2)
library(GGally)
#> Registered S3 method overwritten by 'GGally':
#>   method from   
#>   +.gg   ggplot2

Created on 2020-02-10 by the reprex package (v0.3.0)

  • Goal:
    • A way to customize how ggplot2-like objects and the 'right side objects' combine without producing the warning.

From https://r.789695.n4.nabble.com/quot-Incompatible-methods-quot-for-overloaded-operator-td4633362.html, Martin states that there can not be two + methods. 😞

from ?groupGeneric under 'Ops' (of which "+" is one)

       used.  If different methods are found, there is a warning
       about 'incompatible methods': in that case or if no method is
       found for either argument the internal method is used.

I have tried this with my ggmatrix class and can confirm it doesn't work as intended:

Error: (converted from warning) Incompatible methods ("+.ggmatrix", "+.gg") for "+"

I propose that that the add_ggplot method become an S3 method to allow for other classes to control the left side.

For my specific case, I would be able to add a method of add_ggplot.ggmatrix. From here, I can have a hook to the addition and not produce a warning.

cc @hafen

@schloerke
Copy link
Contributor Author

If there is a better approach, I am all for it! Thank you!

@thomasp85
Copy link
Member

ggplot_add() is already a generic which gets called by add_ggplot(). You can provide methods for that instead

@thomasp85 thomasp85 closed this Feb 10, 2020
@schloerke
Copy link
Contributor Author

schloerke commented Feb 10, 2020

@thomasp85 While true, the current state still doesn't work.

ggplot_add is a generic for the right hand side of +.gg.

I'd like a generic for the left hand side of +.gg because I can't add any other + methods like +.ggmatrix.


Is there a way to define my own method if I know the classes of the right and left side?

From ?groupGeneric under Ops

The classes of both arguments are considered in dispatching
any member of this group. For each argument its vector of
classes is examined to see if there is a matching specific
(preferred) or ‘Ops’ method.

@yutannihilation
Copy link
Member

We might consider integrating vctrs with ggplot2 (c.f. Hadley said so hadley/adv-r#1195 (comment)), which will enable double dispatch. Until then, maybe you can use S4 to do double dispatch?

@schloerke
Copy link
Contributor Author

Pulling in @yutannihilation's example which matches my use case. My situation is exactly like p + theme_bw() at the bottom of the chunk.

library(ggplot2)

set.seed(100)
d1 <- data.frame(x = 1:100, y = cumsum(runif(100)))
d2 <- data.frame(x = 1:100, y = cumsum(runif(100)))

plot_all <- function(...) {
  l <- lapply(list(...), function(d) ggplot(d, aes(x, y)) + geom_line())
  l <- unname(l)
  class(l) <- "manyplot"
  l
}

print.manyplot <- function(x, ...) {
  do.call(gridExtra::grid.arrange, x)
}

p <- plot_all(d1, d2)
p

image

`+.manyplot` <- function(e1, e2) {
  l <- lapply(e1, function(x) x + e2)
  class(l) <- "manyplot"
  l
}

p + theme_bw()
#> Warning: Incompatible methods ("+.manyplot", "+.gg") for "+"
#> Error in p + theme_bw(): non-numeric argument to binary operator

maybe you can use S4?

Since both objects are not S4 objects, I believe Opts dispatch will not work.

https://stat.ethz.ch/R-manual/R-devel/library/methods/html/Methods_for_S3.html

However, primitive functions and operators are exceptions: The internal C code will look for S4 methods if and only if the object is an S4 object. S4 method dispatch would be used to dispatch any binary operator calls where either of the operands was an S4 object, for example.

Using an example from Advanced R, the S4 dispatch does not work as expected.

setGeneric("type", function(x) standardGeneric("type"))
#> [1] "type"
setMethod("type", signature("matrix"), function(x) "matrix")
setMethod("type", signature("character"), function(x) "character")

type(letters)
#> [1] "character"
type(matrix(letters, ncol = 2))
#> [1] "matrix"

foo <- structure(list(x = 1), class = "foo")
type(foo) # expected to fail
#> Error in (function (classes, fdef, mtable) : unable to find an inherited method for function 'type' for signature '"foo"'

setOldClass("foo")
setMethod("type", signature("foo"), function(x) "foo")
    
type(foo)
#> [1] "foo"
    
setMethod("+", signature(e1 = "foo", e2 = "numeric"), function(e1, e2) {
  structure(list(x = e1$x + e2), class = "foo")
})
foo + 3
#> Error in foo + 3: non-numeric argument to binary operator

Created on 2020-02-10 by the reprex package (v0.3.0)

@yutannihilation
Copy link
Member

I think I overcame this issue after this, but don't remember the details...

@yutannihilation
Copy link
Member

I think I defined manyplot as a proper S4 class, not sure if this fits in your use case. Anyway, I don't think this is a good place to discuss here. Maybe we should continue on ggobi/ggally#292?

library(ggplot2)

set.seed(100)
d1 <- data.frame(x = 1:100, y = cumsum(runif(100)))
d2 <- data.frame(x = 1:100, y = cumsum(runif(100)))

setClass("manyplot", slots = c(plots = "list"))

plot_all <- function(...) {
  l <- lapply(list(...), function(d) ggplot(d, aes(x, y)) + geom_line())
  l <- unname(l)
  new("manyplot", plots = l)
}

setMethod("show", signature = "manyplot", function(object) {
  do.call(gridExtra::grid.arrange, object@plots)
})

p <- plot_all(d1, d2)
p

setOldClass(c("theme", "gg"))
setMethod("+", signature = c(e1 = "manyplot", e2 = "gg"), function(e1, e2) {
  e1@plots <- lapply(e1@plots, function(x) x + e2)
  e1
})

p + theme_bw()

Created on 2020-02-11 by the reprex package (v0.3.0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants