Подтвердить что ты не робот

Как выровнять обычный ggplot с ограненным в комоде?

Я пытаюсь организовать графики в готовой публикации с помощью пакета cowplot.
Я просто хочу, чтобы панели были одинакового размера и помечены.

Воспроизводимая экспансия

library(ggplot2)
library(cowplot)

gg1 <- ggplot(mtcars)+
        geom_point(aes(x=mpg,y=hp))+
        theme_bw()+
        theme(aspect.ratio=1)

gg2 <- ggplot(mtcars)+
        geom_point(aes(x=mpg,y=hp,fill=cyl))+
        facet_wrap(~cyl,ncol=2)+
        theme_bw()+
        theme(aspect.ratio=1,
              legend.position='none')

output <- plot_grid(gg1,gg2, labels = c('A','B'),label_size = 20)
print(output)

Код создает этот график. введите описание изображения здесь

Как вы можете видеть, ни горизонтальные оси не совпадают, ни верхние края панелей.

Аргумент align из cowplot не работает с гранями графиков.

Любые идеи?

4b9b3361

Ответ 1

Так как это один из самых высоких голосовых вопросов относительно коровьего и сложного выравнивания, я хотел бы указать, что теперь у коровника есть некоторые функции для выравнивания граненых сюжетов. (Я автор пакета.) Однако в этом конкретном случае они не работают!

Например, это работает (используя параметр axis в plot_grid()):

gg1 <- ggplot(mtcars) +
  geom_point(aes(x=mpg, y=hp)) +
  theme_bw()

gg2 <- ggplot(mtcars)+
  geom_point(aes(x=mpg, y=hp, fill=cyl)) +
  facet_wrap(~cyl, ncol=2) +
  theme_bw() +
  theme(legend.position='none')

plot_grid(gg1, gg2, labels = c('A','B'), label_size = 20,
          align = 'h', axis = 'tb')

введите описание изображения здесь

Мы также можем сделать следующее: для получения другого типа выравнивания (в зависимости от того, хотите ли вы, чтобы фасетная линия была подсчитана как часть графика или нет):

plot_grid(gg1, gg2, labels = c('A', 'B'), label_size = 20,
          align = 'h', axis = 'b')

введите описание изображения здесь

Теперь почему я сказал, что это не работает для этого случая? Потому что, если вы посмотрите на исходный код в вопросе, вы увидите, что был параметр theme(aspect.ratio=1), который я удалил. cowplot может выравнивать графики до тех пор, пока вы не навязываете конкретное соотношение сторон, потому что метод, который он использует для выравнивания графиков, обычно изменяет соотношение сторон отдельных графиков.

Ответ 2

Здесь взломайте, пока кто-то не придумает более элегантный ответ: вы можете использовать grid.arrange из пакета gridExtra, чтобы изменить относительные размеры двух графиков, чтобы оси выстроились в линию. Параметр w в приведенном ниже коде - это то, что управляет тем, что, давая левому графику немного больше горизонтальной ширины, тем самым делая его относительно большим по сравнению с правым графиком.

library(gridExtra)

w = 0.512

grid.arrange(gg1, gg2, widths=c(w,1-w), ncol=2)

Вы также можете использовать arrangeGrob и textGrob, чтобы добавить заголовки "A" и "B" к каждому сюжету.

w = 0.512

grid.arrange(arrangeGrob(textGrob("A", x=0.13, gp=gpar(fontface="bold", cex=1.4)), 
                         gg1, heights=c(0.03,0.97)), 
             arrangeGrob(textGrob("B", x=0.13, gp=gpar(fontface="bold", cex=1.4)), 
                         gg2, heights=c(0.03,0.97)),  
             widths=c(w,1-w), ncol=2)

В любом случае вам нужно вручную настроить w, чтобы выстроить графики (что делает этот метод, скажем так, субоптимальным). Соответствующее значение для w изменится в зависимости от физического размера графика. w=0.512, казалось, работал хорошо, когда я сохранил график ниже как png размером 1000 х 500 пикселей.

введите описание изображения здесь

Лучший ответ, вероятно, будет включать что-то, аналогичное этому SO-ответу, но адаптированный для выстраивания фасетированных и необметенных сюжетов (или, в более общем плане, сюжетов, которые не "есть взаимно однозначное соответствие между их составляющими гномами).

Ответ 3

здесь решение, основанное на эта идея

library(ggplot2)
library(grid)
library(gridExtra)
library(gtable)

gtable_frame <- function(g, width=unit(1,"null"), height=unit(1,"null")){
  panels <- g[["layout"]][grepl("panel", g[["layout"]][["name"]]), ]
  ll <- unique(panels$l)
  tt <- unique(panels$t)

  fixed_ar <- g$respect
  if(fixed_ar) { # there lies madness, want to align despite aspect ratio constraints
    ar <- as.numeric(g$heights[tt[1]]) / as.numeric(g$widths[ll[1]])
    height <- width * ar
    g$respect <- FALSE
  }

  core <- g[seq(min(tt), max(tt)), seq(min(ll), max(ll))]
  top <- g[seq(1, min(tt)-1), ]
  bottom <- g[seq(max(tt)+1, nrow(g)), ]
  left <- g[, seq(1, min(ll)-1)]
  right <- g[, seq(max(ll)+1, ncol(g))]

  fg <- nullGrob()
  lg <-  if(length(left))  g[seq(min(tt), max(tt)), seq(1, min(ll)-1)] else fg
  rg <- if(length(right)) g[seq(min(tt), max(tt)), seq(max(ll)+1,ncol(g))] else fg
  grobs = list(fg, g[seq(1, min(tt)-1), seq(min(ll), max(ll))], fg, 
               lg, g[seq(min(tt), max(tt)), seq(min(ll), max(ll))], rg, 
               fg, g[seq(max(tt)+1, nrow(g)), seq(min(ll), max(ll))], fg)
  widths <- unit.c(sum(left$widths), width, sum(right$widths))
  heights <- unit.c(sum(top$heights), height, sum(bottom$heights))
  all <- gtable_matrix("all", grobs = matrix(grobs, ncol=3, nrow=3, byrow = TRUE), 
                       widths = widths, heights = heights)
  all[["layout"]][5,"name"] <- "panel" # make sure knows where the panel is for nested calls
  if(fixed_ar)  all$respect <- TRUE
  all
}


p1 <- ggplot(mtcars)+
  geom_point(aes(x=mpg,y=hp))+
  theme_bw()+
  theme(aspect.ratio=1)

p2 <- ggplot(mtcars)+
  geom_point(aes(x=mpg,y=hp,fill=cyl))+
  facet_wrap(~cyl,ncol=2)+
  theme_bw()+
  theme(aspect.ratio=1,
        legend.position='none')

g1 <- ggplotGrob(p1)
g2 <- ggplotGrob(p2)
fg1 <- gtable_frame(g1)
fg2 <- gtable_frame(g2)
grid.newpage()
grid.draw(cbind(fg1, fg2))

введите описание изображения здесь

Обратите внимание, что функция gtable_frame переносит графики на основе их панелей, но за исключением панелей по дизайну (я нахожу ее более приятной).

Ответ 4

Я просто хочу добавить, что @baptiste создал большое экспериментальное пакетное яйцо, которое выполняет то, что он написал в его ответ:

Установите его из github (https://github.com/baptiste/egg)

library("devtools")
install_github("baptiste/egg")

Тогда просто do

library("egg")
ggarrange(gg1, gg2, ncol=2)

Вы можете добавлять метки вручную:

ap <- ggarrange(gg1,gg2, ncol=2)
ggdraw(ap) + draw_plot_label(label=c("a","b"), x=c(0,0.5), y=c(1,1))

(Когда я попытался сначала добавить ярлыки к отдельным сюжетам, графики не получили должным образом.)