Бок о бок участки с 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")
![введите описание изображения здесь]()