tidyverse / ggplot2

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

Panels are misnamed within tableGrob for `facet_wrap` #6082

Closed nicholasdavies closed 1 week ago

nicholasdavies commented 1 month ago

I found a problem with the naming of panels (naming within the table grob) with facet_wrap; the panels are named in the wrong order. This is not really an issue in the normal course of ggplot2 use, but is confusing for people delving into the tableGrob / gtable structure of a ggplot2 plot.

For example, if you create a plot using facet_wrap() to lay out 2 rows and 3 columns of facets, the panel "cells" of the gtable are named (sensibly) panel-1-1, panel-2-1, panel-3-1, panel-1-2, panel-2-2, and panel-3-2. But the order of these names is wrong, so that the plot as laid out looks like this:

panel-1-1  panel-3-1  panel-2-2
panel-2-1  panel-1-2  panel-3-2

I expected the ordering of names to correspond to this layout:

panel-1-1  panel-2-1  panel-3-1
panel-1-2  panel-2-2  panel-3-2

Here is the code to reproduce the bug:

library(ggplot2)

df <- data.frame(x = 1:6)

plot <- ggplot(df) +
    geom_point(aes(x, x)) +
    facet_wrap(~x, nrow = 2)

gtable <- ggplotGrob(plot)
gtable$layout[grepl("^panel", gtable$layout$name), ]
#>    t  l  b  r z clip      name
#> 1 10  7 10  7 1  off panel-1-1
#> 2 15  7 15  7 1  off panel-2-1
#> 3 10 11 10 11 1  off panel-3-1
#> 4 15 11 15 11 1  off panel-1-2
#> 5 10 15 10 15 1  off panel-2-2
#> 6 15 15 15 15 1  off panel-3-2

Created on 2024-09-05 with reprex v2.1.0

If you look carefully at the output above, you will see how the naming of the panels doesn't correspond to the layout as indicated by t/l/b/r.

In the development version of ggplot2, the issue is in R/facet-.R, lines 238-243:

    # Set panel names
    table$layout$name <- paste(
      "panel",
      rep(seq_len(dim[2]), dim[1]),
      rep(seq_len(dim[1]), each = dim[2]),
      sep = "-"
    )

I believe these lines should instead be

    # Set panel names
    table$layout$name <- paste(
      "panel",
      rep(seq_len(dim[2]), each = dim[1]),
      rep(seq_len(dim[1]), dim[2]),
      sep = "-"
    )

or

    # Set panel names
    table$layout$name <- paste(
      "panel",
      rep(seq_len(dim[1]), dim[2]),
      rep(seq_len(dim[2]), each = dim[1]),
      sep = "-"
    )

(with the two rep lines switched) depending on whether you want the syntax to be panel-col-row or panel-row-col.

teunbrand commented 1 month ago

I can see some utility in having sensible naming. Would you like to prepare a PR to adress this?

nicholasdavies commented 2 weeks ago

Sorry for the delay — have prepared a PR now (#6136)

teunbrand commented 1 week ago

Fixed by #6136