ggplot2 icon indicating copy to clipboard operation
ggplot2 copied to clipboard

Missing generic `+` method

Open schloerke opened this issue 5 years ago • 1 comments

(cc @clauswilke https://github.com/tidyverse/ggplot2/pull/3818#issuecomment-623055651)

Context

Let's say foo is a class defined by a package other than ggplot2 (Ex: patchwork or GGally). foo objects also inherit from gg. bar objects do not inherit from gg

You can currently perform gg_obj + foo_obj as both objects inherit from gg and would produce the same +.gg method from Opt's double dispatch.

Currently, to control how the right-hand-side (foo objects) are added, we can define ggplot_add.foo.

Problem

However, currently we can not customize how left-hand-side objects (ex: foo_obj + gg_obj) are added to gg objects.

Ex: left hand side structures are not ggplot2 objects and will not work with ggplot2 internal methods

x <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
foo_obj <- structure(
 list(x, x), 
  class = c("foo", "gg")
)
# can not customize how the theme is added
foo_obj + theme_bw()

Ex: conflicting + methods make addition impossible

x <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
bar_obj <- structure(
 list(x, x), 
  class = c("bar") # does not inherit from 'gg'
)
`+.bar` <- function(e1, e2) {
  # only add to second plot
  e1[[2]] <- e1[[2]] + e2
  e1
}
# Conflicting `+` methods
bar_obj + theme_bw()
#> Error in bar_obj + theme_bw() : non-numeric argument to binary operator
#> In addition: Warning message:
#> Incompatible methods ("+.bar", "+.gg") for "+" 

Real life situation

  • To get around this in GGally, I overwrite the +.gg method. Unfortunately, this produces a warning and does not dispatch to the next available plus method. I am open to other options, but I don't know how to produce one that would play well with other packages.

  • I would like to be able to add a scale to a ggmatrix object in appropriate places only (Ex: ggmatrix_obj + gg_scale_obj). With the current methods, I don't know how I can do this cleanly.

Goal

Define a generic add method (ex: add_gg(e1, e2)). This generic add method should be called within +.gg to avoid double dispatch confusion involving +.

ggplot2's +.gg is not commutative. So I believe this situation has merit.

Using the implementation in https://github.com/tidyverse/ggplot2/pull/3818 , I would be able to define how a theme object is added to a custom object.

library(ggplot2)
x <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
foo_obj <- structure(
 list(x, x), 
  class = c("foo", "gg")
)
`add_gg.foo` <- function(e1, e2) {
  # only add to second plot
  e1[[2]] <- e1[[2]] + e2
  e1
}
print.foo <- function(x, ...) {
  gridExtra::grid.arrange(x[[1]], x[[2]], ncol = 2)
}

foo_obj + theme_bw()

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

schloerke avatar May 05 '20 13:05 schloerke

I would also appreciate seeing this added to ggplot2 as it will give me a way to insure my new class object is still valid after vanilla ggplot2 layers are added. Currently, I either have to override +.gg or write a ggplot_build method to insure my object is still valid.

jtlandis avatar Jan 21 '21 16:01 jtlandis