3

I am ordering multiple ggplots into several columns using grid library. The resulting plots are being clipped at the right margin.

This code shows the problem:

library(gridExtra)
library(grid)
library(ggplot2)
data <- data.frame(x = 1, y = 1)
p <- ggplot(data, aes(x = x, y = y)) +
  geom_point()
grid.arrange(p, p, p, p, ncol=2)

enter image description here Adding frames around the plots shows that the axis texts are actually not inside the plot areas:

library(gridExtra)
library(grid)
library(ggplot2)
data <- data.frame(x = 1, y = 1)
p <- ggplot(data, aes(x = x, y = y)) +
  geom_point()+
  theme(plot.background = element_rect(
    color = "black",
    fill = NA,
    linewidth = 1)
    )
grid.arrange(p, p, p, p, ncol=2)

https://i.sstatic.net/I34GIkWk.png

Why is this, and how can I keep the plot elements inside the plot area? If this is not possible, how can I get grid to respect the whole chart area, including axis texts?

5
  • 1
    Welcome to SO. Is there a reason you need to use grid::grid.arrange rather than ggplot2::facet_grid or ggplot2::facet_wrap? Commented Jun 8 at 14:47
  • 1
    Try widening the margins, p<-p+theme(plot.margin = unit(c(1,1,1,1), "cm")). Also, you are not loading package gridExtra where grid.arrange can be found, your code doesn't work as posted. Commented Jun 8 at 15:54
  • @RuiBarradas, you are correct, I have added gridExtra now. See also my second post. Commented Jun 8 at 16:14
  • I'm actually using gridExtra and marrangeGrob to create multiple pages with multiple plots on the fly from an excel spreadsheet. The number of pages depends on the number of plots, and the number of plots/page is user defined. I know I can add margins to the plots, but would like it to work dynamically independent of data input. I wasn't even aware ggplot had grid and wrap functions, are these also capable of multipage multiplot on the fly? Commented Jun 8 at 16:48
  • 1
    ggplot2 by itself doesn't do multi-page plots, but ggforce::facet_wrap_paginate() (and *_grid_*) may do what you want. Commented Jun 8 at 16:57

1 Answer 1

2

In my experience, this can occur when it just happens to be that the data range (based on default expansion of 4%) and the "pretty" labels coincidentally create labels immediately on the edge of the plot. Having ggplot2 try special logic that works around this would be more logic than is strictly/usually necessary, and will increase the likelihood that it does not function predictably in all scenarios. (In my opinion, "simple and predictable" is often better and easier to work with.)

In addition to adjusting the margins (already suggested, and you said you are trying to not do that), another technique is to control either breaks= or labels=.

Changing breaks= requires you to determine a logical and "pretty" set of axis ticks, which can be itself a bit of a challenge in some scenarios; that is, if you know what your data range will be, then perhaps it will be easy, but to have a general method that works across number of ticks and ranges and such is a bit more work. base::pretty and scales::breaks_pretty() are good starts for how this can be done, and modifying them to prevent inclusion of 1.05 in this example can take a bit more work.

I recommend going with labels=, and changing any labels that are within "a little" of the expanded range can be replaced with strings. I naively use as.character(.x) for the default display, there are certainly better ways to handle this depending on your real data. I'll leave it to you to control the actual formatting.

# 0.04 is the default in scale-expansion, so we go a touch inside of
# that with 0.03 (subjective)
expanded_range <- scales::expand_range(range(data$x), mul = 0.03)
p <- ggplot(data, aes(x = x, y = y)) +
  geom_point() +
  scale_x_continuous(
    labels = ~ ifelse(.x < expanded_range[1] | .x > expanded_range[2],
                      "", as.character(.x))
  )

ggplot2 grob where x-axis labels that are just on the edges are replaced with empty strings

I'll admit to having done very little testing on this with real-world data; you may find a number other than 0.03 is better in your use.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for this answer! I guess using plot.margin is the easiest for me at this point, and will work perfectly fine 99,99% of the time. That's "good enough", even though I am a bit surprised that ggplot doesn't return an object size containing all of its elements. But considering all the rest of this awsome software I am still very impressed! BTW, my current output really helps me analyze environmental data. I would upvote your answer if I could!
Only one more upvote and you'll have the reputation to upvote!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.