Ответ 1
Ниже приведены шаги, которые помогут вам описать макет:
1) Извлеките легенду как отдельный гроб ( "графический объект" ). Затем мы можем выложить легенду отдельно от графиков.
2) Выровняйте по левому краю края четырех графиков, чтобы левые края и х-весы выстроились правильно. Код, который нужно сделать, получен из этого SO-ответа. Этот ответ имеет функцию для выравнивания произвольного количества графиков, но я не смог заставить это работать, когда я также хотел изменить пропорциональное пространство, выделенное для каждого сюжета, поэтому я закончил тем, что сделал это "длинный путь" настраивая каждый участок отдельно.
3) Разделите графики и легенду с помощью grid.arrange
и arrangeGrob
. Аргумент heights
выделяет разные пропорции полного вертикального пространства для каждого графика. Мы также используем аргумент widths
для выделения горизонтального пространства для графиков в одной широкой колонке и легенды в другой узкой колонке.
4) Постройте устройство в любом размере, который вы желаете. Это то, как вы получаете конкретную форму или соотношение сторон.
library(gridExtra)
library(grid)
# Function to extract the legend from a ggplot graph as a separate grob
# Source: https://stackoverflow.com/a/12539820/496488
get_leg = function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
legend
}
# Get legend as a separate grob
leg = get_leg(site_status)
# Add a theme element to change the plot margins to remove white space between the plots
thm = theme(plot.margin=unit(c(0,0,-0.5,0),"lines"))
# Left-align the four plots
# Adapted from: /info/54626/left-align-two-graph-edges-ggplot/383026#383026
gA <- ggplotGrob(bar + thm)
gB <- ggplotGrob(smoke_status + thm)
gC <- ggplotGrob(hpv_status + thm)
gD <- ggplotGrob(site_status + theme(plot.margin=unit(c(0,0,0,0), "lines")) +
guides(fill=FALSE))
maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5], gC$widths[2:5], gD$widths[2:5])
gA$widths[2:5] <- as.list(maxWidth)
gB$widths[2:5] <- as.list(maxWidth)
gC$widths[2:5] <- as.list(maxWidth)
gD$widths[2:5] <- as.list(maxWidth)
# Lay out plots and legend
p = grid.arrange(arrangeGrob(gA,gB,gC,gD, heights=c(0.5,0.15,0.15,0.21)),
leg, ncol=2, widths=c(0.8,0.2))
Затем вы можете определить форму или соотношение сторон заключительного графика, установив параметры выходного устройства. (Возможно, вам придется настроить размер шрифта при создании базовых графиков, чтобы получить окончательный макет, чтобы выглядеть так, как вы этого хотите.) Сюжет, вставленный ниже, - это png, сохраненный непосредственно из окна графика RStudio. Здесь вы можете сохранить сюжет как файл PDF (но есть много других "устройств", которые вы можете использовать (например, png, jpeg и т.д.) Для сохранения в разных форматах):
pdf("myPlot.pdf", width=10, height=5)
p
dev.off()
Вы также задали вопрос о более эффективном коде. Одна вещь, которую вы можете сделать, это создать список элементов сюжета, которые вы используете несколько раз, а затем просто добавить имя объекта списка к каждому сюжету. Например:
my_gg = list(geom_bar(stat="identity", fill="red"),
theme(legend.position = "none",
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.x = element_blank()),
plot.margin = unit(c(0,0,-0.5,0), "lines"))
smoke_status = ggplot(data, aes(x=pt_id, y=smoke)) +
labs(y="Smoking Status") +
my_gg