Графики рядом с 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
Проверьте stackoverflow.com/questions/31798162/…
Boern
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

пэчворк

Deena
источник
Просто для полноты, пэчворк теперь и на 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)
Thierry
источник
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).

Tung
источник
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.

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

IVIM
источник
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)
Mayank Agrawal
источник
Как ваш ответ улучшает ответ крещения 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