ggplot2 icon indicating copy to clipboard operation
ggplot2 copied to clipboard

POC: Position guides have acces to layer data

Open teunbrand opened this issue 8 months 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