Бок о бок участки с ggplot2

Я хотел бы разместить два графика бок о бок, используя пакет 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()

Ответ 1

Любые 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)

Update

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

Ответ 2

Одним из недостатков решений, основанных на 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")

enter image description here

Объект, который возвращает 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")

enter image description here

Обновления:

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

Ответ 3

Вы можете использовать следующую функцию multiplot из кулинарная книга Winston Chang 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 ))
    }

}

Ответ 4

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

# install.packages("devtools")
devtools::install_github("thomasp85/patchwork")

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

library(patchwork)
p1 + p2

patchwork

Ответ 5

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

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 или изменение формы - я все еще не совсем до скорости на всех этих мощных пакетах Хэдли.

Ответ 6

Используя пакет 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)

Ответ 7

Обновление: Этот ответ очень старый. 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
  }
 }
}

Ответ 8

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)

enter image description here

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

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

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

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

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

Ответ 9

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

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

Ответ 10

Использование 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

enter image description here

Ответ 11

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

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

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

Ответ 12

Пакет 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")

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