Выравнивание ggplot по левому краю при сохранении с фиксированным соотношением сторон

Я создаю специальную тему ggplot, чтобы стандартизировать внешний вид создаваемых мной графиков. Цель более сложная, чем этот минимальный пример, поэтому я ищу общее решение. У меня есть несколько ключевых целей:

  • Я хочу, чтобы все графики экспортировались с одинаковым размером (3000 пикселей в ширину, 1500 пикселей в высоту).
  • Я хочу контролировать соотношение сторон самой панели сюжета.
  • Я хочу использовать textGrobs для включения номеров фигур.
  • Я хочу, чтобы изображение было выровнено по левому краю

Проблема, с которой я столкнулся, заключается в том, что при объединении этих двух ограничений изображение, которое сохраняется, центрирует график ggplot внутри окна, что имеет смысл по умолчанию, но в данном случае выглядит плохо.

Я надеюсь, что есть общее решение - выровнять панель ggplot по левому краю при экспорте. В идеале это также будет работать аналогичным образом для фасетных графов.

Кажется, что-то должно быть возможно с использованием одного или некоторого сочетания пакетов gridExtra, gtable, cowplot и egg, но после нескольких часов экспериментов я немного растерялся. Кто-нибудь знает, как я могу это сделать? Мой код приведен ниже.

Это образ, который создается. Как видите, заголовок выровнен по левому краю внизу, но сам ggplot расположен по центру по горизонтали. Я хочу, чтобы график ggplot выровнялся по левому краю.

Вывод графика: https://i.stack.imgur.com/5EM2c.png

library(ggplot2)

# Generate dummy data
x <- paste0("var", seq(1,10))
y <- LETTERS[1:10]
data <- expand.grid(X=x, Y=y)
data$Z <- runif(100, -2, 2)

# Generate heatmap with fixed aspect ratio
p1 <- ggplot(data, aes(X, Y, fill= Z)) + 
    geom_tile() +
    labs(title = 'A Heatmap Graph') +
    theme(aspect.ratio = 1)


# A text grob for the footer
figure_number_grob <- grid::textGrob('Figure 10',
                                     x = 0.004,
                                     hjust = 0,
                                     gp = grid::gpar(fontsize = 10,
                                                     col = '#01A184'))

plot_grid <- ggpubr::ggarrange(p1,
                               figure_number_grob,
                               ncol = 1,
                               nrow = 2,
                               heights = c(1,
                                           0.05))

# save it
png(filename = '~/test.png', width = 3000, height = 1500, res = 300, type = 'cairo')
print(plot_grid)
dev.off()

person Michael Toth    schedule 14.04.2020    source источник


Ответы (1)


Я смог найти решение, которое работает для моих нужд, хотя это кажется немного взломанным.

Вот основная идея:

  • Создайте график без фиксированного соотношения сторон.
  • Разделите легенду от сюжета как отдельный компонент
  • Используйте функцию расположения GridExtra, чтобы объединить график, разделитель, легенду и еще один разделитель по горизонтали.
  • Set the width of the plot to some fraction of npc (normal parent coordinates), in this case 0.5. This means that the plot will take up 50% of the horizontal space of the output file.
    • Note that this is not exactly the same as setting a fixed aspect ratio for the plot. If you know the size of the output file, it's close to the same thing, but the size of axis text & axis titles will affect the output aspect ratio for the panel itself, so while it gets you close, it's not ideal if you need a truly fixed aspect ratio
  • Установите ширину разделителей равной оставшейся части npc (в данном случае снова 0,5) за вычетом ширины легенды, чтобы горизонтально центрировать легенду в оставшемся пространстве.

Вот мой код:

library(ggplot2)

# Generate dummy data
x <- paste0("var", seq(1,10))
y <- LETTERS[1:10]
data <- expand.grid(X=x, Y=y)
data$Z <- runif(100, -2, 2)

# Generate heatmap WITHOUT fixed aspect ratio. I address this below
p1 <- ggplot(data, aes(X, Y, fill= Z)) + 
    geom_tile() +
    labs(title = 'A Heatmap Graph')

# Extract the legend from our plot
legend = gtable::gtable_filter(ggplotGrob(p1), "guide-box")


plot_output <- gridExtra::arrangeGrob(
    p1 + theme(legend.position="none"),                           # Remove legend from base plot
    grid::rectGrob(gp=grid::gpar(col=NA)),                        # Add a spacer
    legend,                                                       # Add the legend back
    grid::rectGrob(gp=grid::gpar(col=NA)),                        # Add a spacer
    nrow=1,                                                       # Format plots in 1 row
    widths=grid::unit.c(unit(0.5, "npc"),                         # Plot takes up half of width
                        (unit(0.5, "npc") - legend$width) * 0.5,  # Spacer width
                        legend$width,                             # Legend width
                        (unit(0.5, "npc") - legend$width) * 0.5)) # Spacer width

# A text grob for the footer
figure_number_grob <- grid::textGrob('Figure 10',
                                     x = 0.004,
                                     hjust = 0,
                                     gp = grid::gpar(fontsize = 10,
                                                     col = '#01A184'))

plot_grid <- ggpubr::ggarrange(plot_output,
                               figure_number_grob,
                               ncol = 1,
                               nrow = 2,
                               heights = c(1,
                                           0.05))

# save it
png(filename = '~/test.png', width = 3000, height = 1500, res = 300, type = 'cairo')
print(plot_grid)
dev.off()

А вот изображение на выходе: https://i.stack.imgur.com/rgzFy.png < / а>

person Michael Toth    schedule 17.04.2020