Я хотел бы разместить два графика бок о бок, используя пакет 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()
Функция 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)
(Аргумент ncol = 2
сообщает save_plot()
, что есть два графика бок о бок, а save_plot()
делает сохраненное изображение вдвое большим.)
Более подробное описание того, как расположить графики в сетке, см. эту виньетку. Существует также виньетка, объясняющая, как сделать графики с общая легенда.
Одна из частых путаниц заключается в том, что пакет cowplot изменяет тему ggplot2 по умолчанию. Пакет ведет себя так, потому что он был первоначально написан для внутренних лабораторных целей, и мы никогда не используем тему по умолчанию. Если это вызывает проблемы, вы можете использовать один из следующих трех подходов для их работы:
1. Задайте тему вручную для каждого сюжета. Я считаю хорошей практикой всегда указывать конкретную тему для каждого сюжета, как и в + theme_bw()
в приведенном выше примере. Если вы укажете конкретную тему, тема по умолчанию не имеет значения.
2. Верните тему по умолчанию обратно в ggplot2 по умолчанию. Вы можете сделать это с помощью одной строки кода:
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")
Вы можете использовать следующую функцию 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 ))
}
}
Да, важно, чтобы вы правильно упорядочили свои данные. Один из способов:
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 или изменение формы - я все еще не совсем до скорости на всех этих мощных пакетах Хэдли.
Используя пакет 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)
Стивен Тернер разместил функцию arrange()
в блоге Получение Генетики (см. сообщение для инструкций по применению); однако grid.arrange()
рекомендуется, см. комментарий ниже)
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
}
}
}
grid.arrange
(жаль, что я не размещал ее в списках рассылки в то время, когда нет возможности обновить эти онлайн-ресурсы), лучше выбрать упакованную версию, если вы спросите меня
ggplot2 основан на сетке графики, которые предоставляют другую систему для размещения графиков на странице. Команда par(mfrow...)
не имеет прямого эквивалента, поскольку объекты сетки (называемые grobs) не обязательно выводятся немедленно, но могут быть сохранены и обработаны как обычные объекты 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()
. Для общего обзора различных вариантов и некоторого исторического контекста эта виньетка предлагает дополнительную информацию.
Пакет 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")
Вышеупомянутые решения могут быть неэффективными, если вы хотите построить несколько графиков 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
.
В противном случае, надеюсь, вы найдете полезную функцию.
Используя tidyverse
x <- rnorm(100)
eps <- rnorm(100,0,.2)
df <- as.data.frame(cbind(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
as.data.frame(cbind())
, просто используйте data.frame()