Bug in facetted ggplot with coord.fixed()

graphics R graphics R markdown R tricks

I think this is a bug, others disagree!

Chris Evans https://www.psyctc.org/R_blog/ (PSYCTC.org)https://www.psyctc.org/psyctc/
2024-07-10

Explanation

The issue is that I get a lot of unwanted white space above and below facetted plots but not with plots without the facetting.

Show code
valN <- 2000
valCorr <- .7
matCov <- matrix(c(1, valCorr, valCorr, 1), byrow = TRUE, ncol = 2)
vecMeans <- c(0, 0)
MASS::mvrnorm(n = valN, mu = vecMeans, Sigma = matCov) %>%
  as_tibble(.name_repair = "universal") %>%
  rename(Pre = `...1`,
         Post = `...2`) %>%
  mutate(ID = row_number(),
         group = ID %% 2) -> tibDat

Simple plot: no unnecessary white space.

Show code
ggplot(data = tibDat,
       aes(x = Pre, y = Post)) +
  geom_point(alpha = .1) +
  geom_smooth() +
  xlim(c(-4, 4)) +
  ylim(c(-4, 4)) +
  coord_fixed()

Add facets: no unwanted white space.

Show code
ggplot(data = tibDat,
       aes(x = Pre, y = Post)) +
  facet_grid(cols = vars(group),
             space = "free",
             ) +
  geom_point(alpha = .1) +
  geom_smooth() +
  xlim(c(-4, 4)) +
  ylim(c(-4, 4)) 

Adding coord_fixed() causes the issue

This happens after adding facetting and adding coord_fixed().

Show code
ggplot(data = tibDat,
       aes(x = Pre, y = Post)) +
  facet_grid(cols = vars(group),
             space = "free",
             ) +
  geom_point(alpha = .1) +
  geom_smooth() +
  xlim(c(-4, 4)) +
  ylim(c(-4, 4)) +
  coord_fixed()

Same with theme(aspect.ratio = 1)

There was a suggestion on the web, but about a slightly different issue about scales = "free" in the facetting that using theme(aspect.ratio = 1) was better than coord_fixed(). That doesn’t change things for me.

Show code
ggplot(data = tibDat,
       aes(x = Pre, y = Post)) +
  facet_wrap(facets = vars(group)) +
  geom_point(alpha = .1) +
  geom_smooth() +
  theme(aspect.ratio = 1)

Using `theme(plot.margin = …): no good

Show code
ggplot(data = tibDat,
       aes(x = Pre, y = Post)) +
  facet_grid(cols = vars(group),
             space = "free",
             ) +
  geom_point(alpha = .1) +
  geom_smooth() +
  xlim(c(-4, 4)) +
  ylim(c(-4, 4)) +
  coord_fixed() +
  theme(plot.margin = margin(t = 0,
                             b = 0))

Using theme(plot.margin = margin(t = -70, b = -70)) also has no effect.

Show code
ggplot(data = tibDat,
       aes(x = Pre, y = Post)) +
  facet_grid(cols = vars(group),
             space = "free",
             ) +
  geom_point(alpha = .1) +
  geom_smooth() +
  xlim(c(-4, 4)) +
  ylim(c(-4, 4)) +
  coord_fixed() +
  theme(plot.margin = margin(t = -70,
                             b = -70))

Set margin units explicitly with theme(plot.margin = unit(c(-5, 5, -5, 5), "cm")), still no joy on the vertical space though it can be seen that the right and left margins are imposed correctly.

Show code
ggplot(data = tibDat,
       aes(x = Pre, y = Post)) +
  facet_grid(cols = vars(group),
             space = "free",
             ) +
  geom_point(alpha = .1) +
  geom_smooth() +
  xlim(c(-4, 4)) +
  ylim(c(-4, 4)) +
  coord_fixed() +
  theme(plot.margin = unit(c(-5, 5, -5, 5), "cm"))

Solution/bodge courtesy of stefan & axeman!

The solution is to use save the graphic to a file and then to use knitr::plot_crop() to remove the white space from the graphic and then embed the cropped file where you want it. Here’s the code:

Show code
ggplot(data = tibDat,
       aes(x = Pre, y = Post)) +
  facet_grid(cols = vars(group),
             space = "free",
             ) +
  geom_point(alpha = .1) +
  geom_smooth() +
  xlim(c(-4, 4)) +
  ylim(c(-4, 4)) +
  coord_fixed() -> tmpPlot

ggsave(filename = paste0(postDir, "tmpPlot.png"), plot = tmpPlot)
knitr::plot_crop(paste0(postDir, "tmpPlot.png")) -> tmpPng

ggsave(filename = paste0(postDir, "tmpPlot.jpg"), plot = tmpPlot)
knitr::plot_crop(paste0(postDir, "tmpPlot.jpg")) -> tmpJpg

knitr insertion, PNG

With alternative text, i.e. ![cropped_image](`r tmpPng`)

cropped_image

Without alternative text, i.e. ![](`r tmpPng`)

knitr insertion, JPEG

With alternative text, i.e. ![cropped_image](`r tmpJpg`) cropped_image

Without alternative text, i.e. ![](`r tmpJpg`)

Direct insertion

For the png file, using <img src=`r tmpPng` alt=“MyPng”> MyPng

For the png file, using <img src=`r tmpJpg` alt=“MyJpg”> MyJpg

Conclusion

I got the knitr::plot_crop() solution from the answers to my stackoverflow post from stefan and axeman. Thanks to them for that. I have unpacked the details about using knitr::plot_crop() from the pointers they gave to make it easier for others hitting this to make the fix. It’s largely semantic but I disagree with them and still feel that this is a bug or very much out of the grammar of graphics style of ggplot that this is the only way to fix this. To me coord_fixed() is a modification of an “inner plot” when applied after facetting and should apply to the aspect ratio of the facets not to the containing plot. Maybe I will submit it as a request for ggplot but I’m out of energy and time for that now and will just update this file and respond to the stackoverflow post.

Links

Visit count

widget counter

Dates

Last updated

Show code
cat(paste(format(Sys.time(), "%d/%m/%Y"), "at", format(Sys.time(), "%H:%M")))
11/07/2024 at 19:52

Reuse

Text and figures are licensed under Creative Commons Attribution CC BY-SA 4.0. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".

Citation

For attribution, please cite this work as

Evans (2024, July 10). Chris (Evans) R SAFAQ: Bug in facetted ggplot with coord.fixed(). Retrieved from https://www.psyctc.org/R_blog/posts/2024-07-10-bug-in-facetted-ggplot-with-coordfixed/

BibTeX citation

@misc{evans2024bug,
  author = {Evans, Chris},
  title = {Chris (Evans) R SAFAQ: Bug in facetted ggplot with coord.fixed()},
  url = {https://www.psyctc.org/R_blog/posts/2024-07-10-bug-in-facetted-ggplot-with-coordfixed/},
  year = {2024}
}