Call setup_data from parent class

2019-08-05 14:13发布

Backround

I was reading this excellent answer on how to place self adjusting text into bars: Resizeable text grobs inside bars

After reading a bit on ggproto and especially the vignette on Extending ggplot I was wondering why the author had to define the setup_data routine as follows:

GeomFit <- ggproto("GeomFit", GeomRect,
               setup_data = function(data, params) {
                 data$width <- data$width %||%
                   params$width %||% (resolution(data$x, FALSE) * 0.9)
                 transform(data,
                           ymin = pmin(y, 0), ymax = pmax(y, 0),
                           xmin = x - width / 2, xmax = x + width / 2, width = NULL
                 )
               })

Because this is essentially a copy paste from ggplot2::GeomBar:

GeomBar$setup_data
# <ggproto method>
#   <Wrapper function>
#     function (...) 
# f(...)

#   <Inner function (f)>
#     function (data, params) 
# {
#     data$width <- data$width %||% params$width %||% (resolution(data$x, 
#         FALSE) * 0.9)
#     transform(data, ymin = pmin(y, 0), ymax = pmax(y, 0), xmin = x - 
#         width/2, xmax = x + width/2, width = NULL)
# }

So I thought I could replace this simply by:

GeomFit <- ggproto("GeomFit", GeomRect,
                   setup_data = function(self, data, params)
                      ggproto_parent(GeomBar, self)$setup_data(data, params))

This approach works, but I have my doubts whether this may lead to some unwanted behaviour simply becasue the parent class of GeomFit is GeomRect and not GeomBar.

Question

Is it ok (in the sense of: there are no conditions where this may cause a problem) to use ggproto_parent to call a function from a class which is not the parent class of my ggproto object? Why does ggproto_parent have a parent argument in the first place? Shouldn't the parent be anyways determined by the second argument of ggproto?

1条回答
我只想做你的唯一
2楼-- · 2019-08-05 14:22

I'm not involved in the ggplot2 package's development, but I'll take a stab at this since it's been a week & no one else has posted so far...

Below are the relevant function definitions I copied off ggplot2's GitHub page:

ggproto_parent <- function(parent, self) {
  structure(list(parent = parent, self = self), class = "ggproto_parent")
}

`$.ggproto_parent` <- function(x, name) {
  res <- fetch_ggproto(.subset2(x, "parent"), name)
  if (!is.function(res)) {
    return(res)
  }

  make_proto_method(.subset2(x, "self"), res)
}

From the first function, we can deduce that ggproto_parent(GeomBar, self) in this case would return a list of two items, list(parent = GeomBar, self = self) (where self resolves to GeomFit). This list is passed to the second function as x.

The second function then searches within the x[["parent"]] for a method corresponding to the given name, in this case setup_data (fetch_ggproto), and associates it with x[["self"]] if the function in question includes a self parameter (make_proto_method).

Neither function checks for the parent of GeomFit (which can be accessed at GeomFit$super()), so I don't think having GeomBar instead of GeomRect in ggproto_parent() really matters.


That said, I would probably have used something like this:

GeomFit <- ggproto("GeomFit", GeomRect,
                   setup_data = .subset2(GeomBar, "setup_data"))

or this:

GeomFit <- ggproto("GeomFit", GeomRect,
                   setup_data = environment(GeomBar$setup_data)$f)

Which make for shorter code.

查看更多
登录 后发表回答