6

Following up on a recent question of mine, this one is a bit different and illustrates the problem more fully using simpler examples. Below are two data sets and three functions. The first one draws some points and a circle as expected:

library("ggplot2")
library("grid")

td1 <- data.frame(x = rnorm(10), y = rnorm(10))

tf1 <- function(df) { # works as expected
    p <- ggplot(aes(x = x, y = y), data = df)
    p <- p + geom_point(color = "red")
    p <- p + annotation_custom(circleGrob())
    print(p)
}

tf1(td1)

This next one seems to ask for the exact sample plot but the code is slightly different. It does not give an error but does not draw the circle:

tf2 <- function(df) { # circle isn't draw, but no error either
    p <- ggplot()
    p <- p + geom_point(data = df, aes(x = x, y = y), color = "red")        
    p <- p + annotation_custom(circleGrob())
    print(p)
    }

tf2(td1)

Finally, this one involves a more complex aesthetic and gives an empty layer when you try to create the circle:

td3 <- data.frame(r = c(rnorm(5, 5, 1.5), rnorm(5, 8, 2)),
    f1 = c(rep("L", 5), rep("H", 5)), f2 = rep(c("A", "B"), 5))

tf3 <- function(df) {
    p <- ggplot()
    p <- p + geom_point(data = df, 
        aes(x = f1, y = r, color = f2, group = f2))     
#   p <- p + annotation_custom(circleGrob()) # comment out and it works
    print(p)
    }

tf3(td3)

Now, I suspect the problem here is not the code but my failure to grasp the inner workings of ggplot2. I could sure use an explanation of why the circle is not drawn in the 2nd case and why the layer is empty in the third case. I looked at the code for annotation_custom and it has a hard-wired inherit.aes = TRUE which I think is the problem. I don't see why this function needs any aesthetic at all (see the docs on it). I did try several ways to override it and set inherit.aes = FALSE but I was unable to fully penetrate the namespace and make it stick. I tried to example the objects created by ggplot2 but these proto objects are nested very deeply and hard to decipher.

Community
  • 1
  • 1
Bryan Hanson
  • 6,055
  • 4
  • 41
  • 78
  • why not submit this as a new issue on the github repository? – baptiste Jan 19 '13 at 20:33
  • @baptiste I was thinking about that, but wanted to put it here first to see if it was my misunderstanding/incorrect expectation, since I have a history of that. I will submit tomorrow unless someone comes up with an explanation (and since you are quite knowledgeable about these things, I rather doubt anyone will). Thanks. – Bryan Hanson Jan 19 '13 at 20:47
  • I'm posting this to the ggplot2 issue tracker. – Bryan Hanson Jan 20 '13 at 21:54
  • The issue in question is [aesthetic inheritance and annotation_custom](https://github.com/hadley/ggplot2/issues/756) – Faheem Mitha May 13 '13 at 08:22
  • @BryanHanson: Is http://stackoverflow.com/q/14391183/350713 related? I see this is also asked by you. I am running into a similar problem - see http://stackoverflow.com/q/16501999/350713 – Faheem Mitha May 13 '13 at 08:25
  • @FaheemMitha It could be, but I've not really played with legends much. I see you've been digging extensively trying to figure this out. I have had real trouble writing ggplot2 functions in the new version (0.9.3), so much so that I set it aside and re-wrote the functions in lattice (shudder). But at least in lattice things are what they appear to be. I kept chasing my tail in ggplot2 0.9.3 where things were not what they seemed to be. One of the big changes I observed was with stat_summary which they changed, then changed back due to problems; I need it to be more stable! Good Luck! – Bryan Hanson May 13 '13 at 11:25
  • @BryanHanson: Thanks for the comment. I've concluded that `annotation_custom` is buggy, but it is a fairly new feature (less than a year old), and it seems not many people are using it, so perhaps that is not surprising. I don't know whether my issue is a duplicate of yours, so I will submit a separate issue. I agree ggplot2 (at least the more advanced features) is buggy, but it is still relatively new, and quite complex and ambitious. Hopefully (if there is enough developer manpower), things will settle down eventually. I assume lattice is more straightforward. Less magic and more manual. – Faheem Mitha May 13 '13 at 11:31
  • Did you find a solution to this? I have a very complicated ggplot and want to give it a very simple `annotation_custom` – stevec May 09 '20 at 15:01
  • 1
    @stevec Haven't touched this in years, sorry. Be sure you are on the latest `ggplot2` version. – Bryan Hanson May 09 '20 at 21:12
  • @BryanHanson I am on the latest cran version. did you find any other way to achieve what you were attempting? – stevec May 09 '20 at 21:13
  • @stevec Extensive discussion [here](https://github.com/tidyverse/ggplot2/issues/756) but this is ancient by ggplot2 standards. I'd suggest you make an MWE and ask a new question here. – Bryan Hanson May 09 '20 at 21:30

1 Answers1

2

To answer this :

"I don't see why this function needs any aesthetic at all".

In fact annotation_custom need x and y aes to scale its grob, and to use after the native units. Basically it did this :

  x_rng <- range(df$x, na.rm = TRUE)                            ## ranges of x :aes x
  y_rng <- range(df$y, na.rm = TRUE)                            ## ranges of y :aes y
  vp <- viewport(x = mean(x_rng), y = mean(y_rng),              ##  create a viewport
                 width = diff(x_rng), height = diff(y_rng),
                 just = c("center","center"))
  dd <- editGrob(grod =circleGrob(), vp = vp)                  ##plot the grob in this vp 

To illustrate this I add a grob to a dummy plot used as a scale for my grob. The first is a big scale and the second is a small one.

base.big   <- ggplot(aes(x = x1, y = y1), data = data.frame(x1=1:100,y1=1:100))
base.small <- ggplot(aes(x = x1, y = y1), data = data.frame(x1=1:20,y1=1:1))

I define my grob, see I use the native scales for xmin,xmax,ymin,ymax

annot <- annotation_custom(grob = circleGrob(),  xmin = 0, 
                                                 xmax = 20, 
                                                 ymin = 0, 
                                                 ymax = 1)

Now see the scales difference(small point / big circle) between (base.big +annot) and (base.small + annot).

library(gridExtra)
grid.arrange(base.big+annot,
             base.small+annot)

enter image description here

agstudy
  • 119,832
  • 17
  • 199
  • 261
  • Thanks for your answer. I understand the code you wrote and why it works, but I'm not sure how it relates to my question. The reason I commented about not needing an aesthetic is that the function does not use an aesthetic (clear from the docs and code). Certainly it must be scaled to fit the vp, but that's not an aesthetic (the problem may be I am not using these terms correctly). `annotation_custom` does use `inherit.aes` (internally, not an argument) but where it is inheriting from is unclear to me. – Bryan Hanson Jan 19 '13 at 15:43
  • `x` and `y` have default values, which is why you don't have to pass them. – baptiste Jan 19 '13 at 20:31
  • You wrote "Now see the scales difference(small point / big circle) between (base.big +annot) and (base.big + annot)." I'm guessing you meant to write "(base.small + annot)". Also, "Bascially" is misspelt (third line). – Faheem Mitha May 31 '13 at 09:19