2

I want to save multiple ggplots in one pdf. That works pretty well. The problem is that the different plots do have different y axis texts. The result is a different length of the actual plot (x and y axes) due to the fact, that the y axis description has different lengths. I need to hard code somehow the exact width of the x axis so the plot stops misaligning.

Here's an example of the problem: Link

Here's the reproducable code:

library(dplyr)
library(ggplot2)

v1 <- c(1, 1, 1); v2 <- c(1, 0, 0)
v3 <- c(1, 0, 1); v4 <- c(0, 1, 1)
v5 <- c(1, 0, 1); xG <- c(3, 3, 3, 3, 3)

input <- c(v1, v2, v3, v4, v5)

df <- data_frame(values = input, 
             module = c(rep("A", length(v1)), 
                        rep("B", length(v2)), 
                        rep("C", length(v3)), 
                        rep("D", length(v4)), 
                        rep("E", length(v5))))

perWorkField <- df %>%
  group_by(module) %>%
  summarise(sums = sum(values)) %>%
  mutate(percent = round((sums / xG) * 100, 2))

a <- ggplot() + 
  geom_point(data = perWorkField, mapping = aes(x = percent, y = module)) +
  xlim(c(0, 100))

df2 <- perWorkField %>% mutate(module = replace(module, module == "A", "ThisIsALongText"))

b <- ggplot() + 
 geom_point(data = df2, mapping = aes(x = percent, y = module)) + 
 xlim(c(0, 100)) 

pdf("test.pdf")
print(list(a, b))
dev.off()
pogibas
  • 27,303
  • 19
  • 84
  • 117
j3ypi
  • 1,497
  • 16
  • 21

2 Answers2

1

Solution 1:

Add newlines (\n) to ylab (i.e. ylab("module\n\n\n\n\n")). Like this you will increase space between axis title and plot.

a <- ggplot() + 
  geom_point(data = perWorkField, mapping = aes(x = percent, y = module)) +
  xlim(c(0, 100)) +
  ylab("module\n\n\n\n\n")

Solution 2:

Very dirty hack: add white space to your text. Problem with my solution is that you have to adjust nWhiteSpace manually (ie., it's not equal to nchar("ThisIsALongText")).

nWhiteSpace <- 24
foo <- paste(c(rep(" ", nWhiteSpace), "A"), collapse = "")
foo
[1] "                        A"


df1 <- perWorkField %>% mutate(module = replace(module, module == "A", foo))

a <- ggplot() + 
  geom_point(data = df1, mapping = aes(x = percent, y = module)) +
  xlim(c(0, 100))

df2 <- perWorkField %>% mutate(module = replace(module, module == "A", "ThisIsALongText"))

b <- ggplot() + 
 geom_point(data = df2, mapping = aes(x = percent, y = module)) + 
 xlim(c(0, 100)) 

pdf("test.pdf")
print(list(a, b))
dev.off()

enter image description here

pogibas
  • 27,303
  • 19
  • 84
  • 117
  • Thanks. I only have 5 different plots per file so this would still be reliazable. I'm surprised there isn't a more convenient function, though. – j3ypi Sep 14 '17 at 11:50
  • 1
    @jeypeu I updated my answer with cleaner, but still hacky solution. – pogibas Sep 14 '17 at 11:57
1

This solution is based on this post:

Get width of plot area in ggplot2

I extracted x-axis length for both plots and then offset the wider plot by the difference of both x-axis lengths.

EDIT:

Turns out that this solution is not perfect. Plots are slightly offset, so this solution also requires manual fiddling.

library(dplyr)
library(ggplot2)
library(grid)

v1 <- c(1, 1, 1); v2 <- c(1, 0, 0)
v3 <- c(1, 0, 1); v4 <- c(0, 1, 1)
v5 <- c(1, 0, 1); xG <- c(3, 3, 3, 3, 3)

input <- c(v1, v2, v3, v4, v5)

df <- data_frame(values = input, 
                 module = c(rep("A", length(v1)), 
                            rep("B", length(v2)), 
                            rep("C", length(v3)), 
                            rep("D", length(v4)), 
                            rep("E", length(v5))))

perWorkField <- df %>%
  group_by(module) %>%
  summarise(sums = sum(values)) %>%
  mutate(percent = round((sums / xG) * 100, 2))

a <- ggplot() + 
  geom_point(data = perWorkField, mapping = aes(x = percent, y = module)) +
  xlim(c(0, 100))

a
current.vpTree()
seekViewport('xlab-t.4-4-4-4')
x_axis_a <- convertWidth(unit(1,'npc'), 'inch', TRUE)
x_axis_a

df2 <- perWorkField %>% mutate(module = replace(module, module == "A", "ThisIsALongText"))

b <- ggplot() + 
  geom_point(data = df2, mapping = aes(x = percent, y = module)) + 
  xlim(c(0, 100)) 

b
current.vpTree()
seekViewport('xlab-t.4-4-4-4')
x_axis_b <- convertWidth(unit(1,'npc'), 'inch', TRUE)
x_axis_b

a <- a + theme(plot.margin = unit(c(1,1,1,1+abs(x_axis_b - x_axis_a)),"inch"))
b <- b + theme(plot.margin = unit(c(1,1,1,1),"inch"))

pdf("test.pdf")
print(list(a, b))
dev.off()

Feels quite hacky but does the trick for me.

PejoPhylo
  • 469
  • 2
  • 11
  • When I create the pdf with your code its pretty close, but not exactly the same size, which seems a bit odd to me since you did the same thing twice. – j3ypi Sep 14 '17 at 11:59
  • 1
    You are right, it is indeed slightly offset. I didn't notice that. Will edit my answer to mention that. – PejoPhylo Sep 14 '17 at 12:01