ggplot2 icon indicating copy to clipboard operation
ggplot2 copied to clipboard

POC: Position guides have acces to layer data

Open teunbrand opened this issue 1 year ago • 1 comments

This PR aims to fix #6247.

Briefly, it adds the plumbing to let panel data reach the get_layer_key() method of position guides.

I've put this in as a draft, because I'm not entirely sure that we should take this approach. It has some overhead because it needs to split up data by panel, making code slower. We can illustrate with this benchmark:

devtools::load_all("~/packages/ggplot2")
#> ℹ Loading ggplot2

p <- ggplot(mpg, aes(displ, hwy, colour = drv)) +
  geom_point() +
  facet_grid(year ~ cyl)

bench::mark(ggplotGrob(p), min_iterations = 10)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 1 × 6
#>   expression         min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 ggplotGrob(p)    101ms    105ms      6.51    10.8MB     11.7

Created on 2025-02-21 with reprex v2.1.1

The exact same code on the main branch gives this result:

#> # A tibble: 1 × 6
#>   expression         min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 ggplotGrob(p)   81.3ms   82.3ms      7.71    10.5MB     12.3

To illustrate that it works, we can assemble a quick-and-dirty guide extension that displays a density line for the data it sees, instead of the actual axis line.

devtools::load_all("~/packages/ggplot2")
#> ℹ Loading ggplot2

GuideDensity <- ggproto(
  "GuideDensity", GuideAxis,
  get_layer_key = function(params, layers, data = NULL, theme = NULL) {
    vals <- unlist(lapply(data, function(d) d[[params$aesthetic]]))
    dens <- density(vals, from = min(vals), to = max(vals))
    params$decor <- data.frame(x = dens$x, z = dens$y / max(dens$y))
    params
  },
  build_decor = function(decor, grobs, elements, params) {
    element_grob(
      elements$line,
      x = unit(decor$x, "npc"),
      y = unit(1 - decor$z, "npc")
    )
  }
)

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  guides(x = new_guide(super = GuideDensity, available_aes = "x")) +
  theme(axis.line = element_line())

Created on 2025-02-21 with reprex v2.1.1

teunbrand avatar Feb 21 '25 13:02 teunbrand

In addition, it is not entirely clear what data the guide should recieve in terms of facetting and free scales. For example, in grid facets, an x-axis guide for the first column should perhaps see all the data in that column, but that doesn't quite hold for facet_wrap(). All in all I'm not convinced by the goal here.

teunbrand avatar Feb 21 '25 13:02 teunbrand

I don't think it is possible to satisfactorily allow x/y position guides access to the data.

teunbrand avatar Oct 07 '25 14:10 teunbrand