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

Я пытаюсь организовать графики для публикации с использованием пакета 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 не работает с многогранными графиками.

Любые идеи?


person ikashnitsky    schedule 29.08.2015    source источник
comment
Да, забыл упомянуть, как я теперь обхожу проблему. Я сохраняю график без меток, указывающих ширину и высоту как 2x:1x. Это дает достаточно хорошее соответствие. Затем я добавляю метки в графическом редакторе.   -  person ikashnitsky    schedule 29.08.2015


Ответы (5)


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

Например, это работает (используя опцию 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 может выравнивать графики до тех пор, пока вы не форсируете конкретное соотношение сторон, потому что метод, который он использует для выравнивания графиков, обычно изменяет соотношение сторон отдельных графиков.

person Claus Wilke    schedule 24.11.2017

Вот хак, пока кто-нибудь не придумает более элегантный ответ: вы можете использовать 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 x 500 пикселей.

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

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

person eipi10    schedule 29.08.2015

вот решение, основанное на этой идее

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 оборачивает графики на основе их панелей, но исключает полосы панелей по дизайну (мне это кажется более приятным).

person baptiste    schedule 01.04.2016

Обновление: пакет egg теперь находится в CRAN
https://cran.r-project.org/web/packages/egg/index.html


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

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

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

Тогда просто сделайте

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))

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

person Melkor.cz    schedule 10.03.2017

У меня есть более простое решение, основанное на plot_grid и исходном примере. Однако некоторым может показаться, что это немного обман. Можно точно настроить выравнивание графиков с помощью cowplot:plot_grid, добавив вложенные графики NULL и отрегулировав их соотношение высоты и ширины. Это применяется ниже:

gg3<-plot_grid(NULL,gg2, NULL, align = 'h', nrow = 3, rel_heights = c(0.06,1,0.06))
plot_grid(gg1,gg3, labels = c('A','B'),label_size = 20)

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

person Scientist    schedule 15.12.2019
comment
Это прекрасно работает в таких ситуациях, когда выравнивание осей просто не работает. - person Scientist; 15.12.2019