ggplot2
ggplot2 copied to clipboard
Axes at interior panels #4064
This PR aims to implement drawing axes at interior facets proposed in #4064. It implements this for facet_wrap()
as well as for facet_grid()
. While the changes for facet_wrap()
are quite minimal, for facet_grid()
some additional code was borrowed from facet_wrap()
to draw additional axes.
A few discussion points:
- With regards to the main challenge (the name of the argument), I went with
draw.axes
because it felt somewhat more intuitive than justaxes
to me. I'm not particularly attached to the name though. - I also think the unit tests might still be improved, they now require drawing four plots which doesn't make the tests particularly lightweight.
- Lastly, https://github.com/tidyverse/ggplot2/issues/4064#issuecomment-646564182 mentions an option for omitting the labels at interior axes. I could add this, but wanted to ask if the maintainers approve of this first.
Two small demonstrations:
library(ggplot2)
df <- data.frame(
x = 1, y = 1, facet = LETTERS[1:4]
)
ggplot(df, aes(x, y)) +
geom_point() +
facet_wrap(vars(facet), draw.axes = "all")
df <- data.frame(
x = 1:4, y = 1:4,
fx = c("A", "A", "B", "B"),
fy = c("c", "d", "c", "d")
)
ggplot(df, aes(x, y)) +
geom_point() +
facet_grid(vars(fy), vars(fx), draw.axes = "all")
Created on 2021-05-05 by the reprex package (v1.0.0)
When I need this functionality I rely on: https://cran.r-project.org/web/packages/lemon/vignettes/facet-rep-labels.html It offers different ways to specify how axes are repeated
I have also some version of this in an extension package of mine: https://teunbrand.github.io/ggh4x/articles/Facets.html#extended-facets-1 But I'll gladly make it unnecessary through incorporation in ggplot2.
Agree having this in ggplot2 will make it more mainstream I use ggh4x a lot especially the facet_nested ! Thank you !
@clauswilke any thoughts on this - I'm in two minds
I like this feature. My workaround, which I use all the time, is to set scales to free and then impose scale limits. This has the same effect visually but is a bit confusing, as it requires setting two contradictory options. I'd also say that being able to omit labels for interior plots would be useful.
I haven't reviewed the code, though.
The current interface seems like a reasonable compromise to me; it meaningfully improves the display options available, without getting bogged down into too many details. However, I do think axes
fits better with the current argument names than draw.axes
.
@teunbrand do you mind changing to axes
and then merging/rebasing against main?
@clauswilke do you want to review this? or should I?
Thanks Hadley! I'll take a look in a few days time once I've got some to spare myself.
option for omitting the labels at interior axes
Revisiting this topic now a while later, I don't think there currently exists a clean path towards omitting labels. We can edit the axis grob in the draw_panels()
method, but it feels a bit messy. While I think it is useful to be able to omit labels, the messy approach might be more difficult to maintain in the future, and the faceting code is already quite complicated.
What I think is a neater solution would be to create an argument in the render_axes()
function that propagates all the way down to ggplot2:::guide_gengrob.axis()
, that controls the drawing of the label parts. I could give this a go, but it might be a good idea to postpone this and consider implementing #3329 first.
So this PR has been barren for a while, but with ggproto guides, we can now also implement the censoring of labels. I've called the argument that controls this axis_labels
but perhaps there is a more intuitive name. Quick demo:
devtools::load_all("~/packages/ggplot2/")
#> ℹ Loading ggplot2
base <- ggplot(mtcars, aes(mpg, disp)) +
geom_point()
base + facet_grid(vars(cyl), vars(vs), axes = "all", axis_labels = "margins")
base + facet_wrap(vars(cyl, vs), axes = "all", axis_labels = "margins")
Created on 2023-05-21 with reprex v2.0.2
As you can see for the facet_wrap()
case, it is mildly annoying that the labels in the x-axis of the rightmost panel still contributes height for spacing panels. I could try to make this slightly better, but I don't know if that is needed.
Is the additional height only seen if all axes are shown (i.e. turning off interior axes will revert completely to the old look/spacing)?
Yeah it looks like it should if no interior axes are drawn or the interior axes are drawn with labels. The issue is that the space reserved for axes is calculated as the maximum size for all the axes in a particular row or column. We could try to calculate the size of only the relevant axes that are in between panels, but this would get pretty gnarly and the drawing code already isn't the easiest to work with.
Now with much better spacing for hidden labels:
devtools::load_all("~/packages/ggplot2/")
#> ℹ Loading ggplot2
ggplot(mtcars, aes(mpg, disp)) +
geom_point() +
guides(x.sec = "axis", y.sec = "axis") +
facet_wrap(vars(cyl, vs), axes = "all", axis_labels = "margins")
Created on 2023-10-31 with reprex v2.0.2
A small update on how this PR now interacts with newer features.
When using stacked axes, only the first axis in the stack is rendered at interior panels without labels if so directed.
devtools::load_all("~/packages/ggplot2/")
#> ℹ Loading ggplot2
p <- ggplot(mtcars, aes(mpg, disp)) +
geom_point() +
facet_wrap(vars(cyl, vs), axes = "all", axis_labels = "margins")
p + guides(x = guide_axis_stack("axis", "axis"),
y = guide_axis_stack("axis", "axis"))
An exterior r-axis in coord_radial()
is treated as typical axes, i.e. labels are censored.
p + coord_radial()
Whereas interior r-axes are rendered in full.
p + coord_radial(end = 1.5 * pi)
Created on 2023-12-12 with reprex v2.0.2
Thanks for the review Thomas!