Выровнять по левому краю два ребра графа (ggplot)


105

Я использую ggplot и у меня есть два графика, которые я хочу отображать друг над другом. Я использовал grid.arrangeот gridExtra, чтобы складывать их. Проблема в том, что я хочу, чтобы левые края графиков выравнивались, а также правые края независимо от меток осей. (проблема возникает из-за того, что метки одного графика короткие, а другого длинные).

Вопрос:
как я могу это сделать? Я не женат на grid.arrange, но ggplot2 просто необходим.

Что я пробовал:
я попытался поиграть с шириной и высотой, а также с ncol и nrow, чтобы создать сетку 2 x 2 и разместить визуальные элементы в противоположных углах, а затем поиграть с шириной, но мне не удалось получить визуальные эффекты в противоположных углах .

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
grid.arrange(A, B, ncol=1)

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


2
Вот два возможных варианта: здесь и здесь .
joran 08

@Joran Я ищу выравнивание левых осей. Я не думаю, что у них это получится. Хотя я бы хотел ошибаться.
Тайлер Ринкер

Ответы:


132

Попробуй это,

 gA <- ggplotGrob(A)
 gB <- ggplotGrob(B)
 maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
 gA$widths[2:5] <- as.list(maxWidth)
 gB$widths[2:5] <- as.list(maxWidth)
 grid.arrange(gA, gB, ncol=1)

редактировать

Вот более общее решение (работает с любым количеством графиков) с использованием модифицированной версии, rbind.gtableвключенной вgridExtra

gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))

3
Красиво и действительно довольно прямолинейно. Спасибо за решение.
Тайлер Ринкер

1
Отличное решение! Я искал что-то подобное, чтобы выровнять несколько отдельных графиков временных рядов, которые я не могу сделать с фасетированием из-за основных настроек в каждом графике.
wahalulu

Не могли бы вы представить, как будет соответствовать высота, если у нас будет две колонны? gA $ heights [2: 3] не работает. Должен ли я выбирать другой элемент гроба, кроме 2: 3? Спасибо!
Etienne Low-Décarie

4
Спасибо за решение Батист. Однако я не могу заставить это работать, когда один из сюжетов представляет собой файл tableGrob. gtable::cbindДает мне неутешительную ошибку: nrow(x) == nrow(y) is not TRUE. Какие-либо предложения?
Габра

2
Это решение сработало для меня, я пытаюсь понять это. Для чего нужен [2:5]стенд?
Hurlikus

38

Я хотел обобщить это для любого количества сюжетов. Вот пошаговое решение с использованием подхода Батиста:

plots <- list(A, B, C, D)
grobs <- list()
widths <- list()

собрать ширину для каждого гроба каждого участка

for (i in 1:length(plots)){
    grobs[[i]] <- ggplotGrob(plots[[i]])
    widths[[i]] <- grobs[[i]]$widths[2:5]
}

используйте do.call, чтобы получить максимальную ширину

maxwidth <- do.call(grid::unit.pmax, widths)

назначьте максимальную ширину для каждого гроба

for (i in 1:length(grobs)){
     grobs[[i]]$widths[2:5] <- as.list(maxwidth)
}

участок

do.call("grid.arrange", c(grobs, ncol = 1))

2
Работает даже тогда, когда на сюжетах есть легенды разной ширины - очень красиво!
Кейт Хьюитт

30

Использование пакета cowplot :

A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 

library(cowplot)
plot_grid(A, B, ncol=1, align="v")

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


12

На http://rpubs.com/MarkusLoew/13295 доступно действительно простое решение (последний пункт), применяемое к этой проблеме:

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
grid.draw(rbind(ggplotGrob(A), ggplotGrob(B), size="first"))

вы также можете использовать это как для ширины, так и для высоты:

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
C <- ggplot(CO2, aes(x=conc)) + geom_bar() +coord_flip()
D <- ggplot(CO2, aes(x=uptake)) + geom_bar() +coord_flip() 
grid.draw(cbind(
            rbind(ggplotGrob(A), ggplotGrob(B), size="first"),
            rbind(ggplotGrob(C), ggplotGrob(D), size="first"),
            size='first'))

2
использование size="first"означает, что выравнивание будет выглядеть не очень хорошо, если второй участок больше первого,
baptiste

10

В eggпакете обручи ggplot объекты в стандартизированной 3x3gtable, что позволяет выравнивание сюжетных панелей между произвольным ggplots, в том числе граненых из них.

library(egg) # devtools::install_github('baptiste/egg')
library(ggplot2)

p1 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
  geom_point() 

p2 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
  geom_point() + facet_wrap( ~ cyl, ncol=2, scales = "free") +
  guides(colour="none") +
  theme()

ggarrange(p1, p2)

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


для меня это могло правильно расположить по горизонтали простую тепловую карту ( geom_tile) с легендой внизу и многогранную тепловую карту ( facet_gridс geom_tile), но не удалось выровнять высоту третьего графика, который был дендрограммой ( geom_segment). однако, коровий заговор или gridExtra::grid.arrangeне смогли сделать даже первое, так что пока это работает лучше всего,
deeenes

8

Вот еще одно возможное решение с использованием meltпакета reshape2 и facet_wrap:

library(ggplot2)
library(reshape2)

dat = CO2[, c(1, 2)]
dat$id = seq(nrow(dat))
mdat = melt(dat, id.vars="id")

head(mdat)
#   id variable value
# 1  1    Plant   Qn1
# 2  2    Plant   Qn1
# 3  3    Plant   Qn1
# 4  4    Plant   Qn1
# 5  5    Plant   Qn1
# 6  6    Plant   Qn1

plot_1 = ggplot(mdat, aes(x=value)) + 
         geom_bar() + 
         coord_flip() +
         facet_wrap(~ variable, nrow=2, scales="free", drop=TRUE)

ggsave(plot=plot_1, filename="plot_1.png", height=4, width=6)

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


Это решение предполагает, что у вас одинаковое количество строк в каждом столбце. В моем MRWE это правда, но не на самом деле.
Тайлер Ринкер

Я не уверен, что понимаю: вы имеете в виду, что CO2 $ Plant и CO2 $ Type имеют одинаковую длину, но ваши фактические данные не такие?
bdemarest 08

Это два разных набора данных, которые используют одну переменную, поэтому количество строк не совпадает.
Тайлер Ринкер

2

Пакет patchwork обрабатывает это по умолчанию:

library(ggplot2)
library(patchwork)

A <- ggplot(CO2, aes(x = Plant)) + geom_bar() + coord_flip() 
B <- ggplot(CO2, aes(x = Type)) + geom_bar() + coord_flip() 

A / B

Создано 08.12.2019 пакетом REPEX (v0.3.0)


0

В лучшем случае это взлом:

library(wq)
layOut(list(A, 1, 2:16),  list(B, 2:3, 1:16))

Хотя это действительно неправильно.


-1

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

library(purrr)
list(A, B) %>% 
  map(ggplotGrob) %>% 
  do.call(gridExtra::gtable_rbind, .) %>% 
  grid::grid.draw()
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.