tidyverse / ggplot2

An implementation of the Grammar of Graphics in R
https://ggplot2.tidyverse.org
Other
6.47k stars 2.02k forks source link

Data independent/relative scales for `annotation_custom()` #5417

Closed half-normal closed 9 months ago

half-normal commented 1 year ago

Perhaps there is already a way to do this, but can we somehow get data independent scales for the annotation_custom() function.

I will often want to produce the same plot for different response variables and and want an icon in the top left corner. I know I could do this by getting the max of the y variable and setting ymin/ymax relative to that. However, it would be nice to just have a 0-1 coordinate system that sits over any plot, whether it be a single plot, combination of plots, facet wrap plots, etc. Then annotations can be added at the end in a consistent way.

smouksassi commented 1 year ago

You can use npc units: https://stackoverflow.com/questions/63741925/use-npc-units-in-annotate

And see this https://search.r-project.org/CRAN/refmans/ggpp/html/annotate.html

half-normal commented 1 year ago

@smouksassi As far as I can tell the first only works for text labels, the second works nicely, but not for faceted plots (puts the annotation in every facet). What I am after is a simple way of putting PNG icons/pictures on a plot in a way which is independent of the plot itself (i.e. does not depend on what is in the plot such as axis scales, facets, etc).

smouksassi commented 1 year ago

Ok care to share some code or mock end results you are after?

teunbrand commented 1 year ago

See also #5290 for proposed mechanism of scale-independent aesthetics.

half-normal commented 1 year ago

@smouksassi here is a function I wrote which does something like what I am after. A couple limitations are that it can't be added (with +) to the plot like a regular ggplot layer and it only plots within the x and y axis bounds. I will probably look into how to change the latter when I get some time.

annotate_grob <- function(plot, grob, xmin, xmax, ymin, ymax, data_scale = FALSE){
  library(ggplot2)
  if (!data_scale) {
    # Min and max values from plot axes
    min_x <- layer_scales(plot)$x$range$range[1]
    max_x <- layer_scales(plot)$x$range$range[2]
    min_y <- layer_scales(plot)$y$range$range[1]
    max_y <- layer_scales(plot)$y$range$range[2]
    x_len <- max_x - min_x
    y_len <- max_y - min_y
    # Create grob boundaries as scaled versions of min/max values
    xmin <- min_x + x_len*xmin
    xmax <- min_x + x_len*xmax
    ymin <- min_y + y_len*ymin
    ymax <- min_y + y_len*ymax
  }
  plot + annotation_custom(
    grob, xmin, xmax, ymin, ymax
  )
}
aphalo commented 12 months ago

You may want to try geom_text_npc() or geom_label_npc() from package 'ggpp'. They are geometries, so they work with facets, even with free scales. They use "pseudo aesthetics" npcx and npcy, and internally do computaions similar to those in your mock function. I have included also other geoms based on the same mechanism, even for plots, tables and grobs. 'ggpp' is in CRAN and the documentation is on-line, the documentation contains quite a few examples. These geoms can also be used with 'annotate()' (slightly modified in 'ggpp' so that it accepts and passes npcx and npcy).

teunbrand commented 9 months ago

I consider this issue fixed by #5477.