3
votes

I am switching from basic R plot tools to ggplot2 and am struggling with one issue.

In basic R you can control distance to each of four axes (or the "box") by setting margins. Resulting margins are fixed and do not depend on what you plot. These allows me to produce plots for my papers with exactly same plot area sizes despite the size of tick labels and axis labels.

In ggplot, I ecnountered this (minimum working example):

library(ggplot2)
dat = data.frame(x = 1:5, y = 1e-5* (1:5) ^ 2)
p = ggplot(dat, aes(x, y)) + geom_point() + geom_line()

print(p)
print(p + scale_y_log10())

First figure Second figure

Black arrows at the left-hand side of the plots show the difference between actual margins I get. Axis label(y) stays in place, while position of the y-axis shifts depending on the size of tick labels (text representation). It can be further escalated by changing axis.text.y to e.g. increase size.

What I desire is to be able to control actual margins no matter what tick labels are drawn - in that case I can achieve same sizes of figures of different data sets.

3

3 Answers

2
votes

Although there are many theme options in ggplot2, there does not appear to be an option which sets a fixed margin space for the axes (or if there is it is well hidden). The cowplot package has an align_plots function which can align one or both axes in a list of plots. align_plots returns a list, each component of which is the original plot but with the axes specified aligned. I am using the grid.arrange function from the gridExtra package to output both plots so you can see the way the alignment works:

library(ggplot2)
dat = data.frame(x = 1:5, y = 1e-5* (1:5) ^ 2)
p = ggplot(dat, aes(x, y)) + geom_point() + geom_line()

print(p)

p1 = p + scale_y_log10()
print(p1)

library(cowplot)
library(gridExtra)
p2 = align_plots(p, p1, align = "hv")
grid.arrange(p2[[1]], p2[[2]])

enter image description here

This is how the two original plots would have output:

grid.arrange(p, p1)

enter image description here

2
votes

Following the approach suggested by Stewart Ross in this message, I ended up in the similar thread. I played around with grobs generated from my sample ggplots using this method - and was able to determine how to manually control layout of your grobs individually (at least, to some extent).

For a sample plot, a generated grob's layout looks like this:

> p1$layout
   t l  b r  z clip       name
17 1 1 10 7  0   on background
1  5 3  5 3  5  off     spacer
2  6 3  6 3  7  off     axis-l
3  7 3  7 3  3  off     spacer
4  5 4  5 4  6  off     axis-t
5  6 4  6 4  1   on      panel
6  7 4  7 4  9  off     axis-b
7  5 5  5 5  4  off     spacer
8  6 5  6 5  8  off     axis-r
9  7 5  7 5  2  off     spacer
10 4 4  4 4 10  off     xlab-t
11 8 4  8 4 11  off     xlab-b
12 6 2  6 2 12  off     ylab-l
13 6 6  6 6 13  off     ylab-r
14 3 4  3 4 14  off   subtitle
15 2 4  2 4 15  off      title
16 9 4  9 4 16  off    caption

Here we are interested in 4 axes - axis-l,t,b,r. Suppose we want to control left margin - look for axis-l. Notice that this particular grob has a layout of 7x10.

p1$layout[p1$layout$name == "axis-l", ]
  t l b r z clip   name
2 6 3 6 3 7  off axis-l

As far as I understood it, this output means that left axis takes one grid cell (#3 horizontally, #6 vertically). Note index ind = 3. Now, there are two other fields in grob - widths and heights. Lets go to widths (which appears to be a specific list of grid's units) and pick up width with index ind we just obtained. In my sample case the output is something like

> p1$widths[3]
[1] sum(1grobwidth, 3.5pt)

I guess it is a 'runtime-determined' size of some 1grobwidth plus additional 3.5pt. Now we can replace this value by another unit (I tested very simple things like centimeters or points), e.g. p1$widths[3] = unit(4, "cm"). So far I was able to confirm that if you specify equal 'widths' for left axis of two diferent plots, you will get identical margins.

Exploring $layout table might provide other ways of controlling plot layout (e.g. look at the $layout$name == "panel" to change plot area size).

1
votes

produce plots for my papers with exactly same plot area sizes

this might help:

grid::grid.draw(egg::set_panel_size(ggplot2::qplot(1,1), width = grid::unit(3, "in")))

enter image description here