SkyWalker
SkyWalker

Reputation: 14309

ggplot2: How to crop out of the blank area on top and bottom of a plot?

This is a follow up of Question How to fit custom long annotations geom_text inside plot area for a Donuts plot?. See the accepted answer, the resulting plot understandably has extra blank area on the top and on the bottom. How can I get rid of those extra blank areas? I looked at theme aspect.ratio but this is not what I intend though it does the job but distorts the plot. I'm after cropping the plot from a square to a landscape form.

How can I do that?

UPDATE This is a self contained example of my use-case:

library(ggplot2); library(dplyr); library(stringr)

df <- data.frame(group = c("Cars", "Trucks", "Motorbikes"),n = c(25, 25, 50),
                 label2=c("Cars are blah blah blah", "Trucks some of the best in town", "Motorbikes are great if you ..."))
df$ymax = cumsum(df$n)
df$ymin = cumsum(df$n)-df$n
df$ypos = df$ymin+df$n/2
df$hjust = c(0,0,1)

ggplot(df %>%
       mutate(label2 = str_wrap(label2, width = 10)), #change width to adjust width of annotations
       aes(x="", y=n, fill=group)) +
   geom_rect(aes_string(ymax="ymax", ymin="ymin", xmax="2.5", xmin="2.0")) +
   expand_limits(x = c(2, 4)) + #change x-axis range limits here
   # no change to theme
   theme(axis.title=element_blank(),axis.text=element_blank(),
         panel.background = element_rect(fill = "white", colour = "grey50"),
         panel.grid=element_blank(),
         axis.ticks.length=unit(0,"cm"),axis.ticks.margin=unit(0,"cm"),
         legend.position="none",panel.spacing=unit(0,"lines"),
         plot.margin=unit(c(0,0,0,0),"lines"),complete=TRUE) +
   geom_text(aes_string(label="label2",x="3",y="ypos",hjust="hjust")) +
   coord_polar("y", start=0) + scale_x_discrete()

And this is the result I'd like to find an answer to fix those annotated resulting blank spaces:

Donuts plot

Upvotes: 7

Views: 6642

Answers (2)

www
www

Reputation: 4224

This is a multi-part solution to answer this and the other related question you've posted.

First, for changing the margins in a single graph, @Keith_H was on the right track; using plot.margin inside theme() is a convenient way. However, as mentioned, this alone won't solve the issue if the goal is to combine multiple plots, as in the case of the other question linked above.

To do that you'll need a combination of plot.margin and a specific plotting order within arrangeGrob(). You'll need a specific order because plots get printed in the order you call them, and because of that, it will be easier to change the margins of plots that are layered behind other plots, instead of in front of plots. We can think of it like covering the plot margins we want to shrink by expanding the plot on top of the one we want to shrink. See the graphs below for illustration:

Before plot.margin setting:

enter image description here

#Main code for the 1st graph can be found in the original question.

After plot.margin setting:

enter image description here

#Main code for 2nd graph:
ggplot(df %>%
           mutate(label2 = str_wrap(label2, width = 10)),
                  aes(x="", y=n, fill=group)) +
  geom_rect(aes_string(ymax="ymax", ymin="ymin", xmax="2.5", xmin="2.0")) +
  geom_text(aes_string(label="label2",x="3",y="ypos",hjust="hjust")) +
  coord_polar(theta='y') +
  expand_limits(x = c(2, 4)) + 
  guides(fill=guide_legend(override.aes=list(colour=NA))) +
  theme(axis.line = element_blank(),
        axis.ticks=element_blank(),
        axis.title=element_blank(),
        axis.text.y=element_blank(),
        axis.text.x=element_blank(),
        panel.border = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_rect(fill = "white"),
        plot.margin = unit(c(-2, 0, -2, -.1), "cm"),
        legend.position = "none") +
 scale_x_discrete(limits=c(0, 1))

After combining plot.margin setting and arrangeGrob() reordering:

enter image description here

#Main code for 3rd graph:
p1 <- ggplot(mtcars,aes(x=1:nrow(mtcars),y=mpg)) + geom_point()

p2 <- ggplot(df %>%
               mutate(label2 = str_wrap(label2, width = 10)), #change width to adjust width of annotations
                      aes(x="", y=n, fill=group)) +
        geom_rect(aes_string(ymax="ymax", ymin="ymin", xmax="2.5", xmin="2.0")) +
        geom_text(aes_string(label="label2",x="3",y="ypos",hjust="hjust")) +
        coord_polar(theta='y') +
        expand_limits(x = c(2, 4)) + #change x-axis range limits here
        guides(fill=guide_legend(override.aes=list(colour=NA))) +
        theme(axis.line = element_blank(),
              axis.ticks=element_blank(),
              axis.title=element_blank(),
              axis.text.y=element_blank(),
              axis.text.x=element_blank(),
              panel.border = element_blank(),
              panel.grid.major = element_blank(),
              panel.grid.minor = element_blank(),
              panel.background = element_rect(fill = "white"),
              plot.margin = unit(c(-2, 0, -2, -.1), "cm"),
              legend.position = "none") +
        scale_x_discrete(limits=c(0, 1))

final <- arrangeGrob(p2,p1,layout_matrix = rbind(c(1),c(2)),
                     widths=c(4),heights=c(2.5,4),respect=TRUE)

Note that in the final code, I reversed the order you had in the arrangeGrob from p1,p2 to p2,p1. I then adjusted the height of the first object plotted, which is the one we want to shrink. This adjustment allows the earlier plot.margin adjustment to take effect, and as that takes effect, the graph printed last in order, which is P1, will start to take the space of what was the margins of P2. If you make one of these adjustments with out the others, the solution won't work. Each of these steps are important to produce the end result above.

Upvotes: 7

Keith_H
Keith_H

Reputation: 84

You can set the plot.margins to negative values for the top and bottom of the plot.

plot.margin=unit(c(-4,0,-4,0),"cm"),complete=TRUE)

edit: here is the output: cropped

Upvotes: 0

Related Questions