Графики рядом с ggplot2


339

Я хотел бы разместить два графика рядом с помощью пакета ggplot2 , т.е. сделать эквивалент par(mfrow=c(1,2)).

Например, я хотел бы, чтобы следующие два графика показывали бок о бок с одинаковым масштабом.

x <- rnorm(100)
eps <- rnorm(100,0,.2)
qplot(x,3*x+eps)
qplot(x,2*x+eps)

Нужно ли помещать их в один и тот же data.frame?

qplot(displ, hwy, data=mpg, facets = . ~ year) + geom_smooth()

Я думаю, что вы могли бы сделать это с решеткой. Является ли ggplot2 жестким требованием?
— JD Long

8
Нет. Но я уже потратил время на настройку qplots, так что это было так, как мне нравилось. :-) И я пытаюсь поиграться с ggplot.
— Кристофер Дюбуа


1
Для хорошего обзора, см. Виньетка для упаковки яиц : Выкладывание нескольких участков на странице
— Henrik

Ответы:


505

Любые ggplots рядом (или n графиков в сетке)

Функция grid.arrange()в gridExtraпакете объединит несколько графиков; это то, как вы положили два рядом.

require(gridExtra)
plot1 <- qplot(1)
plot2 <- qplot(1)
grid.arrange(plot1, plot2, ncol=2)

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

Это будет выводить результат как побочный эффект. Чтобы напечатать побочный эффект в файл, укажите драйвер устройства (например pdf, pngи т. Д.), Например

pdf("foo.pdf")
grid.arrange(plot1, plot2)
dev.off()

или использовать arrangeGrob()в сочетании с ggsave(),

ggsave("foo.pdf", arrangeGrob(plot1, plot2))

Это эквивалентно использованию двух разных графиков par(mfrow = c(1,2)). Это не только экономит время на организацию данных, это необходимо, когда вы хотите два разных графика.


Приложение: Использование граней

Аспекты полезны для создания похожих сюжетов для разных групп. Это указано ниже во многих ответах ниже, но я хочу выделить этот подход примерами, эквивалентными приведенным выше графикам.

mydata <- data.frame(myGroup = c('a', 'b'), myX = c(1,1))

qplot(data = mydata, 
    x = myX, 
    facets = ~myGroup)

ggplot(data = mydata) + 
    geom_bar(aes(myX)) + 
    facet_wrap(~myGroup)

Обновить

plot_gridфункция в cowplotстоит проверить в качестве альтернативы grid.arrange. Смотрите ответ по @ claus-wilke ниже и эту виньетку для эквивалентного подхода; но функция позволяет более точно контролировать местоположение и размер графика, основываясь на этой виньетке .


2
Когда я запускал ваш код с использованием объектов ggplot, sidebysideplot был нулевым. Если вы хотите сохранить вывод в файл, используйте gridArrange. См. Stackoverflow.com/questions/17059099/…
— Джим

@ Джим, спасибо, что указал на это. Я пересмотрел свой ответ. Дайте мне знать, если остались какие-либо вопросы.
— Дэвид Лебауэр,

1
Теперь grid.aarange ограничен?
— Atticus29

?grid.arrangeзаставляет меня думать, что эта функция теперь называетсяrangeGrob. Я смог сделать то, что хотел, a <- arrangeGrob(p1, p2)и потом print(a).
— Blakeoft

@blakeoft вы смотрели примеры? grid.arrangeвсе еще действительная, не осуждаемая функция. Вы пытались использовать функцию? Что произойдет, если не то, что вы ожидали.
— Дэвид Лебауэр

159

Один из недостатков решений, основанных на grid.arrangeтом, что они затрудняют маркировку графиков буквами (A, B и т. Д.), Как того требует большинство журналов.

Я написал пакет cowplot для решения этой (и нескольких других) проблем, в частности функции plot_grid():

library(cowplot)

iris1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_boxplot() + theme_bw()

iris2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_density(alpha = 0.7) + theme_bw() +
  theme(legend.position = c(0.8, 0.8))

plot_grid(iris1, iris2, labels = "AUTO")

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

plot_grid()Возвращаемый объект - это другой объект ggplot2, и вы можете сохранить его ggsave()как обычно:

p <- plot_grid(iris1, iris2, labels = "AUTO")
ggsave("plot.pdf", p)

Кроме того, вы можете использовать функцию cowplot save_plot(), которая представляет собой тонкую обертку вокруг, ggsave()которая позволяет легко получить правильные размеры для комбинированных участков, например:

p <- plot_grid(iris1, iris2, labels = "AUTO")
save_plot("plot.pdf", p, ncol = 2)

( ncol = 2Аргумент говорит, save_plot()что есть два графика рядом, и save_plot()делает сохраненное изображение в два раза шире.)

Более подробное описание порядка расположения графиков в сетке см. В этой виньетке. Существует также виньетка, объясняющая, как создавать сюжеты с общей легендой.

Частая путаница заключается в том, что пакет cowplot меняет стандартную тему ggplot2. Пакет ведет себя так, потому что изначально был написан для внутреннего использования в лаборатории, и мы никогда не используем тему по умолчанию. Если это вызывает проблемы, вы можете использовать один из следующих трех подходов, чтобы обойти их:

1. Установите тему вручную для каждого сюжета. Я думаю, что это хорошая практика - всегда указывать определенную тему для каждого сюжета, как я это делал + theme_bw()в примере выше. Если вы указываете конкретную тему, тема по умолчанию не имеет значения.

2. Верните тему по умолчанию обратно к ggplot2 default. Вы можете сделать это с помощью одной строки кода:

theme_set(theme_gray())

3. Вызовите функции cowplot, не подключая пакет. Вы также не можете вызывать library(cowplot)или require(cowplot)и вместо этого вызывать функции cowplot, предварительно добавив cowplot::. Например, приведенный выше пример, использующий тему по умолчанию ggplot2:

## Commented out, we don't call this
# library(cowplot)

iris1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_boxplot()

iris2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_density(alpha = 0.7) +
  theme(legend.position = c(0.8, 0.8))

cowplot::plot_grid(iris1, iris2, labels = "AUTO")

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

Обновления:

  • Начиная с версии 1.0, тема ggplot2 по умолчанию больше не изменяется.
  • Начиная с ggplot2 3.0.0, графики могут быть помечены напрямую, см., Например, здесь.

в выводе cowplot происходит удаление фоновой темы, из обоих сюжетов? есть ли альтернатива?
— VAR121

@ VAR121 Да, это одна строка кода. Объяснено в конце первого раздела вводной виньетки: cran.rstudio.com/web/packages/cowplot/vignettes/…
— Клаус Уилк,

Можно ли иметь одинаковую шкалу y для всех графиков с этим пакетом?
— Герман

Вам нужно будет установить шкалу y вручную, чтобы соответствовать. Или подумайте о гранении.
— Клаус Уилк

Вы могли бы установить ggtitle () для каждого графика, прежде чем использовать grid.arrange ()?
— Seanosapien

49

Вы можете использовать следующую multiplotфункцию из книги поваров Winston Chang's R

multiplot(plot1, plot2, cols=2)

multiplot <- function(..., plotlist=NULL, cols) {
    require(grid)

    # Make a list from the ... arguments and plotlist
    plots <- c(list(...), plotlist)

    numPlots = length(plots)

    # Make the panel
    plotCols = cols                          # Number of columns of plots
    plotRows = ceiling(numPlots/plotCols) # Number of rows needed, calculated from # of cols

    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(plotRows, plotCols)))
    vplayout <- function(x, y)
        viewport(layout.pos.row = x, layout.pos.col = y)

    # Make each plot, in the correct location
    for (i in 1:numPlots) {
        curRow = ceiling(i/plotCols)
        curCol = (i-1) %% plotCols + 1
        print(plots[[i]], vp = vplayout(curRow, curCol ))
    }

}

24

Используя пакет заплаток , вы можете просто использовать +оператор:

library(ggplot2)
library(patchwork)

p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))


p1 + p2

пэчворк


Просто для полноты, пэчворк теперь и на CRAN. Надеюсь, вы довольны моей маленькой правкой
— Tjebo

18

Да, я думаю, вам нужно правильно организовать ваши данные. Одним из способов было бы это:

X <- data.frame(x=rep(x,2),
                y=c(3*x+eps, 2*x+eps),
                case=rep(c("first","second"), each=100))

qplot(x, y, data=X, facets = . ~ case) + geom_smooth()

Я уверен, что есть лучшие трюки в plyr или rehape - я все еще не совсем в курсе всех этих мощных пакетов от Хэдли.


16

Используя пакет reshape, вы можете сделать что-то вроде этого.

library(ggplot2)
wide <- data.frame(x = rnorm(100), eps = rnorm(100, 0, .2))
wide$first <- with(wide, 3 * x + eps)
wide$second <- with(wide, 2 * x + eps)
long <- melt(wide, id.vars = c("x", "eps"))
ggplot(long, aes(x = x, y = value)) + geom_smooth() + geom_point() + facet_grid(.~ variable)

12

Также стоит упомянуть пакет multipanelfigure . Смотрите также этот ответ .

library(ggplot2)
theme_set(theme_bw())

q1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
q2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
q3 <- ggplot(mtcars) + geom_smooth(aes(disp, qsec))
q4 <- ggplot(mtcars) + geom_bar(aes(carb))

library(magrittr)
library(multipanelfigure)
figure1 <- multi_panel_figure(columns = 2, rows = 2, panel_label_type = "none")
# show the layout
figure1

figure1 %<>%
  fill_panel(q1, column = 1, row = 1) %<>%
  fill_panel(q2, column = 2, row = 1) %<>%
  fill_panel(q3, column = 1, row = 2) %<>%
  fill_panel(q4, column = 2, row = 2)
figure1

# complex layout
figure2 <- multi_panel_figure(columns = 3, rows = 3, panel_label_type = "upper-roman")
figure2

figure2 %<>%
  fill_panel(q1, column = 1:2, row = 1) %<>%
  fill_panel(q2, column = 3, row = 1) %<>%
  fill_panel(q3, column = 1, row = 2) %<>%
  fill_panel(q4, column = 2:3, row = 2:3)
figure2

Создано 2018-07-06 пакетом представлением (v0.2.0.9000).


9

ggplot2 основан на сеточной графике, которая предоставляет другую систему для размещения графиков на странице. Команда par(mfrow...)не имеет прямого эквивалента, поскольку объекты сетки (называемые гробами ) не обязательно рисуются сразу, но могут быть сохранены и обработаны как обычные объекты R перед преобразованием в графический вывод. Это обеспечивает большую гибкость, чем рисование этой ныне модели базовой графики, но стратегия обязательно немного другая.

Я написал, grid.arrange()чтобы обеспечить простой интерфейс как можно ближе к par(mfrow). В простейшем виде код будет выглядеть так:

library(ggplot2)
x <- rnorm(100)
eps <- rnorm(100,0,.2)
p1 <- qplot(x,3*x+eps)
p2 <- qplot(x,2*x+eps)

library(gridExtra)
grid.arrange(p1, p2, ncol = 2)

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

Дополнительные параметры подробно описаны в этой виньетке .

Одна распространенная жалоба заключается в том, что графики не обязательно выровнены, например, когда они имеют метки осей разного размера, но это grid.arrangeсделано по замыслу: не предпринимает попыток обращаться к объектам ggplot2 в особом случае и обрабатывает их в равной степени с другими гробами (например, решетчатые графики). ). Это просто помещает гробы в прямоугольную схему.

Для особого случая объектов ggplot2 я написал другую функцию ggarrangeс похожим интерфейсом, которая пытается выровнять панели графиков (включая граненые графики) и пытается соблюдать пропорции, когда они определены пользователем.

library(egg)
ggarrange(p1, p2, ncol = 2)

Обе функции совместимы с ggsave(). Для общего обзора различных вариантов и некоторого исторического контекста эта виньетка предлагает дополнительную информацию .


9

Обновление: этот ответ очень старый. gridExtra::grid.arrange()сейчас рекомендуемый подход. Я оставляю это здесь на случай, если это будет полезно.


Стивен Тернер опубликовал эту arrange()функцию в блоге Getting Genetics Done (инструкции см. В посте)

vp.layout <- function(x, y) viewport(layout.pos.row=x, layout.pos.col=y)
arrange <- function(..., nrow=NULL, ncol=NULL, as.table=FALSE) {
 dots <- list(...)
 n <- length(dots)
 if(is.null(nrow) & is.null(ncol)) { nrow = floor(n/2) ; ncol = ceiling(n/nrow)}
 if(is.null(nrow)) { nrow = ceiling(n/ncol)}
 if(is.null(ncol)) { ncol = ceiling(n/nrow)}
        ## NOTE see n2mfrow in grDevices for possible alternative
grid.newpage()
pushViewport(viewport(layout=grid.layout(nrow,ncol) ) )
 ii.p <- 1
 for(ii.row in seq(1, nrow)){
 ii.table.row <- ii.row 
 if(as.table) {ii.table.row <- nrow - ii.table.row + 1}
  for(ii.col in seq(1, ncol)){
   ii.table <- ii.p
   if(ii.p > n) break
   print(dots[[ii.table]], vp=vp.layout(ii.table.row, ii.col))
   ii.p <- ii.p + 1
  }
 }
}

9
По сути, это очень устаревшая версия grid.arrange(жаль, что я не размещал ее в списках рассылки в то время, когда нет возможности обновить эти онлайн-ресурсы), если вы спросите меня, лучше выбрать упакованную версию
— baptiste

4

Использование tidyverse:

x <- rnorm(100)
eps <- rnorm(100,0,.2)
df <- data.frame(x, eps) %>% 
  mutate(p1 = 3*x+eps, p2 = 2*x+eps) %>% 
  tidyr::gather("plot", "value", 3:4) %>% 
  ggplot(aes(x = x , y = value)) + 
    geom_point() + 
    geom_smooth() + 
    facet_wrap(~plot, ncol =2)

df

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


1

Приведенные выше решения могут оказаться неэффективными, если вы хотите построить несколько графиков ggplot с использованием цикла (например, как здесь предлагается: создание нескольких графиков в ggplot с различными значениями оси Y с использованием цикла ), что является желательным шагом для анализа неизвестного ( или большие) наборы данных (например, когда вы хотите построить график всех переменных в наборе данных).

Приведенный ниже код показывает, как это сделать, используя упомянутый выше метод multiplot (), источник которого находится здесь: http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2) :

plotAllCounts <- function (dt){   
  plots <- list();
  for(i in 1:ncol(dt)) {
    strX = names(dt)[i]
    print(sprintf("%i: strX = %s", i, strX))
    plots[[i]] <- ggplot(dt) + xlab(strX) +
      geom_point(aes_string(strX),stat="count")
  }

  columnsToPlot <- floor(sqrt(ncol(dt)))
  multiplot(plotlist = plots, cols = columnsToPlot)
}

Теперь запустите функцию - чтобы получить Счет для всех переменных, напечатанных с использованием ggplot на одной странице

dt = ggplot2::diamonds
plotAllCounts(dt)

Следует отметить следующее:
использование aes(get(strX)), которое вы обычно используете в циклах при работе ggplot, в приведенном выше коде вместо того, aes_string(strX)чтобы НЕ рисовать нужные графики. Вместо этого он построит последний сюжет много раз. Я не понял, почему - это, возможно, придется сделать aesи aes_stringвызвали ggplot.

В противном случае, надеюсь, вы найдете функцию полезной.


1
Обратите внимание, что в вашем коде растут plotsобъекты, в for-loopкоторых крайне неэффективно и не рекомендуется R. Пожалуйста, посмотрите эти замечательные статьи, чтобы узнать, как лучше это сделать: эффективное накопление в R , применение функции к строкам фрейма данных и ориентированные на строки рабочие процессы в R с помощью tidyverse
— Tung

Более эффективный способ перебирать переменные - использовать tidy evaluationподход, который был доступен начиная с ggplot2 v.3.0.0 stackoverflow.com/a/52045613/786542
— Tung

0

По моему опыту gridExtra: grid.arrange отлично работает, если вы пытаетесь генерировать графики в цикле.

Короткий фрагмент кода:

gridExtra::grid.arrange(plot1, plot2, ncol = 2)

Как ваш ответ улучшает ответ крещения 2 декабря 17 года в 4:20? Ваш ответ кажется дубликатом. Проверьте, что делает приемлемый ответ здесь Как ответить
— Питер

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

Я попытался сделать это, используя пакет cowplot, но безуспешно. На моем быстром сканировании никто не упомянул решение для нескольких графиков в цикле for и, следовательно, мой комментарий. Пошлите мне любой комментарий, если я ошибаюсь.
— Mayank Agrawal

Если бы код в вашем ответе содержал цикл for, он был бы другим.
— Питер

Я бы обновил его через неделю здесь и весь проект на Kaggle. Приветствия.
— Mayank Agrawal

-3

cowplotПакет дает вам хороший способ сделать это, таким образом , что костюмы публикации.

x <- rnorm(100)
eps <- rnorm(100,0,.2)
A = qplot(x,3*x+eps, geom = c("point", "smooth"))+theme_gray()
B = qplot(x,2*x+eps, geom = c("point", "smooth"))+theme_gray()
cowplot::plot_grid(A, B, labels = c("A", "B"), align = "v")

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


3
Смотрите также более подробный ответ авторов пакета и логическое обоснование выше stackoverflow.com/a/31223588/199217
— Дэвид LeBauer
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.