Графики рядом с 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()
15 ответов
Любые 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 ниже и эту виньетку для эквивалентного подхода; но функция позволяет более точно контролировать местоположение и размер графика, основываясь на этой виньетке.
Один недостаток решений на основе 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)
(The 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")
Обновление: Начиная с ggplot2 3.0.0, участки могут быть помечены напрямую, см., Например, здесь.
Используя пакет лоскутное, вы можете просто использовать +
оператор:
# 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
Вы можете использовать следующее multiplot
функция из поваренной книги 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 ))
}
}
Да, я думаю, что вы должны организовать ваши данные соответствующим образом. Одним из способов было бы это:
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 есть лучшие трюки - я все еще не совсем в курсе всех этих мощных пакетов от Hadley.
Используя пакет изменения формы, вы можете сделать что-то вроде этого.
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)
Также стоит упомянуть пакет 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).
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()
, Для общего обзора различных вариантов и некоторого исторического контекста эта виньетка предлагает дополнительную информацию.
Обновление: этот ответ очень старый. 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
}
}
}
С помощью 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
Рассмотрим также
ggarrange
от
ggpubr
упаковка. Он имеет много преимуществ, в том числе возможность выравнивания осей между графиками и объединения общих легенд в одну.
По моему опыту, gridExtra:grid.arrange отлично работает, если вы пытаетесь создавать графики в цикле.
Фрагмент сокращенного кода:
gridExtra::grid.arrange(plot1, plot2, ncol = 2)
Приведенные выше решения могут оказаться неэффективными, если вы хотите построить несколько графиков 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)
}
Теперь запустите функцию - чтобы получить Count для всех переменных, напечатанных с использованием ggplot на одной странице
dt = ggplot2::diamonds
plotAllCounts(dt)
Стоит отметить, что:
с помощью aes(get(strX))
, который вы обычно используете в циклах при работе с ggplot
в приведенном выше коде вместо aes_string(strX)
НЕ будет рисовать нужные участки. Вместо этого он построит последний сюжет много раз. Я не понял, почему - возможно, придется сделать aes
а также aes_string
называются в ggplot
,
В противном случае, надеюсь, вы найдете функцию полезной.
И plot_grid, и grid_arrange у меня не работали. И это странно. Когда я пытаюсь построить оба графика по отдельности, я получаю правильные графики, хотя, когда я использую одну из этих функций, упомянутых ранее, я получаю один и тот же график, повторяющийся два раза, вместо двух разных графиков.
vv <- 1
# dump the data for this variable into a new data frame:
df1 <- data.frame(x<-sharkMeanFitX[vv,], y<-sharkMeanFit[vv,], err<-
sharkSDFit[vv,])
# Plot mean fit with error bars of 1 sd for each predictor:
#
#-->When ready to output to a file, uncomment the png and dev.off lines <---
#--> Don't forget to change the file name when you switch variables <--
#png(filenameList[vv],width=10,height=7,res=300,units="cm") #uncomment to print to file!
ggplot(df1, aes(x=x,y=y))+
geom_ribbon(aes(ymin=y-(1.96*err),ymax=y+(1.96*err),alpha=0.1),show.legend=FALSE) +
geom_line(colour="blue", size=1) +
theme(text = element_text(size = 18)) +
xlab("TagID") +
ylab(ylabList[vv])
# pick which variable to plot:
vv <- 2
# dump the data for this variable into a new data frame:
df1 <- data.frame(x<-sharkMeanFitX[vv,], y<-sharkMeanFit[vv,], err<-sharkSDFit[vv,])
# Plot mean fit with error bars of 1 sd for each predictor:
#
#-->When ready to output to a file, uncomment the png and dev.off lines <---
#--> Don't forget to change the file name when you switch variables <--
#png(filenameList[vv],width=10,height=7,res=300,units="cm") #uncomment to print to file!
ggplot(df1, aes(x=x,y=y))+
geom_ribbon(aes(ymin=y-(1.96*err),ymax=y+(1.96*err),alpha=0.1),show.legend=FALSE) +
geom_line(colour="blue", size=1) +
theme(text = element_text(size = 18)) +
xlab("Chl (mg m^-3)") +
ylab(ylabList[vv])
Если у кого-то есть идеи, это будет здорово! заранее спасибо.
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")