Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shared titles and axes #337

Merged
merged 14 commits into from
Jan 4, 2024
Merged

Shared titles and axes #337

merged 14 commits into from
Jan 4, 2024

Conversation

teunbrand
Copy link
Contributor

@teunbrand teunbrand commented Oct 23, 2023

This PR aims to fix #150.

Briefly, it adds options to plot_layout() that allows the user to:

  1. Collect titles, i.e. deduplicate them and merge them
  2. Deduplicate axes

What can and cannot be merged is mostly determined by a homebrew 2D run-length encoding of a simplified layout.

An example using identical plots to showcase the sharing.
Notice the following:

  • The 2-cell plot breaks continuity so nothing is shared 'across' that plot.
  • The rightmost 4 plots share titles and axes, almost acting like facets.
library(ggplot2)
devtools::load_all("~/packages/patchwork/")
#> ℹ Loading patchwork

# loading dev patchwork overrides theme
theme_set(theme_grey())

p1 <- ggplot(mtcars) +
  aes(x = cyl, y = disp) +
  geom_point() +
  guides(x.sec = "axis", y.sec = "axis")

layout <- plot_layout(
  design = c("1234\n5267"),
  collect_titles = "both",
  dedup_axes = "both"
)

p1 + p1 + p1 + p1 + p1 + p1 + p1 + layout

Another example showing not-merge behaviour for the last plot.
Notice the following:

  • Cells [1, 3] and [1,4] two plots on the top right continue sharing y-axes/axis-title.
  • Cells [1, 3] and [2, 3] continue sharing x-axis/axis-title.
p2 <- p1 + theme(axis.text = element_text(colour = "red")) + 
  labs(x = "foo", y = "bar")

p1 + p1 + p1 + p1 + p1 + p1 + p2 + layout

Created on 2023-10-23 with reprex v2.0.2

Why is this PR still WIP?

  • Haven't thought deeply how this functionality will be exposed to user.
  • Other than not merging over nested patches, this approach is totally blind to nested patches. Should it be blind to that?
  • Need to add unit tests, but I'm still looking for a few challenging cases.
  • I have no idea (yet) how this interacts with facetting. Facetted plots don't share titles or axes.
  • Resizing after deleting grobs does not take into account sizes of remaining grobs, as can be seen in the example below where we have long y-axis text. However, this information isn't retained somewhere, so it probably can only be done by measuring the remaining grobs themselves?
p1 <- p1 + scale_y_continuous(labels = \(x) paste0(x, " but with longer stuff")) +
  guides(x.sec = "none", y.sec = "none")

p1 + p1 + p1 + p2 + plot_layout(2, 2, dedup_axes = "y")

@thomasp85
Copy link
Owner

  • Haven't thought deeply how this functionality will be exposed to user.
    • I don't like the dedup_axes name and think the functionality should work in the same way as the guide collecting functionality. This suggests adding a axes and axis_titles argument to plot_layout() that can take "keep" and "collect" as values
  • Other than not merging over nested patches, this approach is totally blind to nested patches. Should it be blind to that?
    • Yes, the axis functionality should only work in one nesting level
  • I have no idea (yet) how this interacts with facetting. Facetted plots don't share titles or axes.
    • axis title collecting can still work as normally but any plot with multiple axes in the collection direction should be ignored
  • Resizing after deleting grobs does not take into account sizes of remaining grobs, as can be seen in the example below where we have long y-axis text. However, this information isn't retained somewhere, so it probably can only be done by measuring the remaining grobs themselves?
    • Yeah, this should be fixed somehow

@teunbrand
Copy link
Contributor Author

Thanks for your thoughts Thomas!

This suggests adding a axes and axis_titles argument to plot_layout() that can take "keep" and "collect" as values

Is there a need to discriminate the x- or y-direction and if so, how would you like this to be exposed? We could add x_axes = NULL and y_axes = NULL arguments, where x_axes = x_axes %||% axes or something. Or should the options be "keep", "collect", "collect_x" and "collect_y"?

@thomasp85
Copy link
Owner

If we should discriminate I think the last suggestion is the best since we already have committed to string values

@teunbrand teunbrand changed the title WIP: Shared titles and axes Shared titles and axes Nov 2, 2023
@teunbrand teunbrand marked this pull request as ready for review November 2, 2023 10:02
@teunbrand
Copy link
Contributor Author

Latest updates:

  • Refurbished arguments so that now axis = 'collect{_x/_y}' can be used and axis_titles in similar fashion.
  • Fixed the spacing issue after large axis is removed.

I think this PR is now in a good place for review.

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

theme_set(theme_grey())

p1 <- ggplot(mtcars) +
  aes(x = cyl, y = disp) +
  geom_point()

p2 <- p1 + 
  theme(axis.text = element_text(colour = "red")) + 
  labs(x = "foo", y = "bar") 

p1 <- p1 + scale_y_continuous(labels = \(x) paste0("a long label indicating ", x))

p1 + p1 + p1 + p2 + plot_layout(2, 2, axes = "collect", axis_titles = "collect")

Created on 2023-11-02 with reprex v2.0.2

@thomasp85 thomasp85 merged commit 686275d into thomasp85:main Jan 4, 2024
@thomasp85
Copy link
Owner

Thank you!

@teunbrand teunbrand deleted the share_titles branch January 4, 2024 09:50
@bwiernik
Copy link
Contributor

bwiernik commented Jan 4, 2024

Amazing!

@pabloabur
Copy link

pabloabur commented Jun 14, 2024

Hi,

It seems like this doesn't do anything when we use | and / instead of plot_layout definition. Is this intended?

p1 <- ggplot(mtcars) +
  aes(x = cyl, y = disp) +
  geom_point()

p2 <- p1 + 
  theme(axis.text = element_text(colour = "red")) + 
  labs(x = "foo", y = "bar") 

p1 <- p1 + scale_y_continuous(labels = \(x) paste0("a long label indicating ", x))

((p1 | p1) / (p1 | p2)) + plot_layout(axes = "collect", axis_titles = "collect")

test

@teunbrand
Copy link
Contributor Author

Is this intended?

Yes and no. The merging mechanism don't work over multiple nesting levels, which is what you've induced with your layout code. We had accepted this limitation, so while we agree that it doesn't work out ideally in your case, it generally does what we had intended. The solution to the problem would be to use an unnested layout design.

@pabloabur
Copy link

Fair enough. Is there a way to raise a warning in this situation? Or alternatively mention it on the documentation? I could make a pull request (although the former seems to be complicated)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Combine axes/scales
4 participants