I am trying to merge multiple ggplot2 plots into a single picture, while retaining control over the respective height of each element. The following code works well and it generates a barplot and a curve plot that are perfectly aligned to each other (see here):
library(ggplot2)
library(grid)
library(gridExtra)
# Save plot as PDF (set to NULL initially)
pdf(file = NULL)
# Data frame for the first plot
df_1 <- data.frame(
data = c(0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.2, 0.2, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.4, 0.2,
0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.5, 0.1, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.2, 0.2,
0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.4, 0.2, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.5, 0.1,
0.5, 0.1, 0.15, 0.5, 0.5, 0.4),
base = 1:54,
value = c(0.9557, 0.4407, 0.141, 3.4229, 3.5197, 4.7386, 6.9897, 0.3958, 4.7268, 1.8085,
10.5312, 5.9011, 7.9393, 5.7363, 13.8539, 11.2096, 14.8407, 11.4008, 0.5412, 10.3042,
11.3208, 15.6669, 8.2177, 12.6917, 20.7124, 10.9896, 24.7246, 7.0544, 21.0489, 16.4536,
7.4753, 28.2106, 32.7284, 29.7557, 23.3567, 29.9186, 30.3813, 15.1307, 20.2323, 34.4262,
20.8036, 18.7994, 41.7036, 26.828, 10.7628, 28.2335, 4.4982, 3.6528, 33.8571, 11.6208,
43.54, 26.7859, 22.2176, 11.0588)
)
# First plot with ggplot
plot_1 <- ggplot(df_1, aes(x = base, y = data, fill = value)) +
geom_bar(stat = 'identity', position = position_dodge(width = 0.9)) +
scale_fill_distiller(palette = 'YlGnBu') +
theme(
axis.text.x = element_text(size = 9, angle = 0, vjust = 0.5),
axis.text.y = element_text(size = 9),
legend.position = 'right',
legend.spacing.x = unit(5, 'pt'),
legend.text = element_text(margin = margin(0, 0, 0, 0, 'pt'), size = 10),
legend.key = element_rect(fill = NA),
axis.title.x = element_blank(),
axis.title.y = element_blank(),plot.margin = margin(0,0,0,0)
) +
guides(
colour = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
shape = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
fill = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5)
)+
xlim(0, 55)
# Data frame for the second plot
df_2 <- data.frame(
x1 = c(4, 12, 1, 2),
x2 = c(9, 15, 24, 54),
y1 = c(0, 0, 0, 0),
y2 = c(0, 0, 0, 0),
priority = c('high', 'low', 'mid', 'high')
)
# Second plot with ggplot
plot_2 <- ggplot(df_2) +
geom_curve(aes(x = x1, xend = x2, y = y1, yend = y2, color = priority),
curvature = 1, linewidth = 0.5, ncp = 1000) +
expand_limits(y = -1) +
coord_fixed(ratio = 22, ylim = c(-1, 0)) +
scale_colour_brewer(palette = 'YlGnBu') +
theme(
legend.position = 'right',
legend.spacing.x = unit(5, 'pt'),
legend.text = element_text(margin = margin(0, 0, 0, 0, 'pt'), size = 10),
legend.key = element_rect(fill = NA),
axis.title.x = element_blank(),
axis.title.y = element_blank(),plot.margin = margin(0,0,0,0)
) +
guides(
colour = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
shape = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
fill = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5)
) +
xlim(0, 55)
# List of ggplot objects
plots <- list(plot_1, plot_2) # Add as many plots as you need
# Apply a small margin to each plot to reduce extra white space
plots_adjusted <- lapply(plots, "+", theme(plot.margin = margin(0, 0, 0, 0)))
# Convert adjusted plots to gtables
grob_plots <- lapply(plots_adjusted, ggplotGrob)
# Calculate the maximum widths across all plots
max_widths <- do.call(unit.pmax, lapply(grob_plots, function(g) g$widths))
# Set each plot's widths to the calculated maximum widths
grob_plots <- lapply(grob_plots, function(g) { g$widths <- max_widths; g })
# Arrange the grobs with specific heights
combined_plot <- arrangeGrob(grobs = grob_plots, heights = unit(c(1,1), "null"), padding = unit(0, "pt"))
dev.off()
# Save the combined plot to a file
ggsave("~/Downloads/mytest.pdf", combined_plot,dpi = 300)
However, when I try to modify the width on the output PDF file in ggsave()
(for example by setting width=20
), the relative proportions of the plots are lost, and only the first one is extended to fill the print area (see here).
Can you advise me on how to fix this?
As pointed out in the comments, combining the plots using patchwork
will keep them aligned. To control the height of each plot, you can use the height
argument in the patchwork::plot_layout
function. However, this only works if you remove the coord_fixed
argument from plot_2
.
library(ggplot2)
library(patchwork)
# Data frame for the first plot
df_1 <- data.frame(
data = c(0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.2, 0.2, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.4, 0.2,
0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.5, 0.1, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.2, 0.2,
0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.4, 0.2, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.5, 0.1,
0.5, 0.1, 0.15, 0.5, 0.5, 0.4),
base = 1:54,
value = c(0.9557, 0.4407, 0.141, 3.4229, 3.5197, 4.7386, 6.9897, 0.3958, 4.7268, 1.8085,
10.5312, 5.9011, 7.9393, 5.7363, 13.8539, 11.2096, 14.8407, 11.4008, 0.5412, 10.3042,
11.3208, 15.6669, 8.2177, 12.6917, 20.7124, 10.9896, 24.7246, 7.0544, 21.0489, 16.4536,
7.4753, 28.2106, 32.7284, 29.7557, 23.3567, 29.9186, 30.3813, 15.1307, 20.2323, 34.4262,
20.8036, 18.7994, 41.7036, 26.828, 10.7628, 28.2335, 4.4982, 3.6528, 33.8571, 11.6208,
43.54, 26.7859, 22.2176, 11.0588)
)
# First plot with ggplot
plot_1 <- ggplot(df_1, aes(x = base, y = data, fill = value)) +
geom_bar(stat = 'identity', position = position_dodge(width = 0.9)) +
scale_fill_distiller(palette = 'YlGnBu') +
theme(
axis.text.x = element_text(size = 9, angle = 0, vjust = 0.5),
axis.text.y = element_text(size = 9),
legend.position = 'right',
legend.spacing.x = unit(5, 'pt'),
legend.text = element_text(margin = margin(0, 0, 0, 0, 'pt'), size = 10),
legend.key = element_rect(fill = NA),
axis.title.x = element_blank(),
axis.title.y = element_blank(),plot.margin = margin(0,0,0,0)
) +
guides(
colour = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
shape = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
fill = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5)
)+
xlim(0, 55)
# Data frame for the second plot
df_2 <- data.frame(
x1 = c(4, 12, 1, 2),
x2 = c(9, 15, 24, 54),
y1 = c(0, 0, 0, 0),
y2 = c(0, 0, 0, 0),
priority = c('high', 'low', 'mid', 'high')
)
# Second plot with ggplot
plot_2 <- ggplot(df_2) +
geom_curve(aes(x = x1, xend = x2, y = y1, yend = y2, color = priority),
curvature = 1, linewidth = 0.5, ncp = 1000) +
expand_limits(y = -1) +
coord_fixed(ratio = 22, ylim = c(-1, 0)) +
scale_colour_brewer(palette = 'YlGnBu') +
theme(
legend.position = 'right',
legend.spacing.x = unit(5, 'pt'),
legend.text = element_text(margin = margin(0, 0, 0, 0, 'pt'), size = 10),
legend.key = element_rect(fill = NA),
axis.title.x = element_blank(),
axis.title.y = element_blank(),plot.margin = margin(0,0,0,0)
) +
guides(
colour = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
shape = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
fill = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5)
) +
xlim(0, 55)
p <- plot_1 / plot_2
# Save the combined plot to a file
ggsave("~/Downloads/mytest.pdf",
p,
width = 20, height = 10)
If you want to adjust the heights, you can do:
p <- plot_1 / plot_2 + plot_layout(heights = c(2,1))
p
To use a large width in ggsave
, one option is to change the aspect ratio of the plot:
p <- plot_1 / plot_2 &
theme(aspect.ratio = 1/10)
ggsave("~/Downloads/mytest.pdf",
p,
width = 40, height = 10)