Gráficos lado a lado com ggplot2

339

Eu gostaria de colocar dois gráficos lado a lado usando o pacote ggplot2 , ou seja, faça o equivalente a par(mfrow=c(1,2)).

Por exemplo, eu gostaria que os dois gráficos a seguir mostrassem lado a lado com a mesma escala.

x <- rnorm(100)
eps <- rnorm(100,0,.2)
qplot(x,3*x+eps)
qplot(x,2*x+eps)

Preciso colocá-los no mesmo data.frame?

qplot(displ, hwy, data=mpg, facets = . ~ year) + geom_smooth()
Christopher DuBois
fonte
Eu acho que você pode fazer isso com treliça. O ggplot2 é um requisito difícil?
JD Longo
8
Não. Mas eu já havia passado o tempo ajustando os qplots, então era do jeito que eu gostava. :-) E eu estou tentando brincar com o ggplot.
Christopher DuBois
11
Para uma boa visão geral, ver a vinheta para o pacote de ovo : Layout de vários gráficos em uma página
Henrik

Respostas:

505

Qualquer gráfico gg lado a lado (ou n gráficos em uma grade)

A função grid.arrange()no gridExtrapacote combinará várias plotagens; é assim que você coloca dois lado a lado.

require(gridExtra)
plot1 <- qplot(1)
plot2 <- qplot(1)
grid.arrange(plot1, plot2, ncol=2)

Isso é útil quando os dois gráficos não se baseiam nos mesmos dados, por exemplo, se você deseja plotar variáveis ​​diferentes sem usar reshape ().

Isso plotará a saída como um efeito colateral. Para imprimir o efeito de um lado para um arquivo, especificar um controlador de dispositivo (tais como pdf, png, etc.), por exemplo,

pdf("foo.pdf")
grid.arrange(plot1, plot2)
dev.off()

ou use arrangeGrob()em combinação com ggsave(),

ggsave("foo.pdf", arrangeGrob(plot1, plot2))

Isso é o equivalente a fazer dois gráficos distintos usando par(mfrow = c(1,2)). Isso não apenas economiza tempo na organização dos dados, como também é necessário quando você deseja dois gráficos diferentes.


Apêndice: Usando facetas

As facetas são úteis para fazer gráficos semelhantes para grupos diferentes. Isso é indicado abaixo em muitas respostas abaixo, mas quero destacar essa abordagem com exemplos equivalentes aos gráficos acima.

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)

Atualizar

a plot_gridfunção no cowplotvale a pena conferir como uma alternativa para grid.arrange. Veja a resposta de @ claus-wilke abaixo e esta vinheta para uma abordagem equivalente; mas a função permite controles mais refinados na localização e tamanho da plotagem, com base nessa vinheta .

David LeBauer
fonte
2
Quando executei seu código usando objetos ggplot, sidebysideplot é nulo. Se você deseja salvar a saída em um arquivo, use gridArrange. Veja stackoverflow.com/questions/17059099/…
Jim
@ Jim obrigado por apontar isso. Eu revi minha resposta. Deixe-me saber se houver alguma dúvida.
David LeBauer
11
O grid.aarange está privado agora?
Atticus29
?grid.arrangeme faz pensar que essa função agora é chamada de arranjoGrob. Eu era capaz de fazer o que queria fazendo a <- arrangeGrob(p1, p2)e depois print(a).
blakeoft
@blakeoft você olhou os exemplos? grid.arrangeainda é uma função válida e não obsoleta. Você tentou usar a função? O que acontece, se não o que você esperava.
David LeBauer
159

Uma desvantagem das soluções baseadas grid.arrangeé que elas dificultam rotular as parcelas com letras (A, B, etc.), como a maioria dos periódicos exige.

Eu escrevi o pacote cowplot para resolver este (e mais alguns) problemas, especificamente a função 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")

insira a descrição da imagem aqui

O objeto que plot_grid()retorna é outro objeto ggplot2, e você pode salvá-lo ggsave()como de costume:

p <- plot_grid(iris1, iris2, labels = "AUTO")
ggsave("plot.pdf", p)

Como alternativa, você pode usar a função cowplot save_plot(), que é um invólucro fino ggsave()que facilita a obtenção das dimensões corretas para plotagens combinadas, por exemplo:

p <- plot_grid(iris1, iris2, labels = "AUTO")
save_plot("plot.pdf", p, ncol = 2)

(O ncol = 2argumento diz save_plot()que há duas plotagens lado a lado e save_plot()torna a imagem salva duas vezes maior.)

Para uma descrição mais detalhada de como organizar plotagens em uma grade, consulte esta vinheta. Há também uma vinheta explicando como fazer gráficos com uma legenda compartilhada.

Um ponto frequente de confusão é que o pacote cowplot altera o tema padrão do ggplot2. O pacote se comporta dessa maneira porque foi originalmente escrito para uso em laboratório interno e nunca usamos o tema padrão. Se isso causar problemas, você pode usar uma das três abordagens a seguir para solucionar isso:

1. Defina o tema manualmente para cada plot. Eu acho que é uma boa prática sempre especificar um tema específico para cada enredo, assim como fiz + theme_bw()no exemplo acima. Se você especificar um tema específico, o tema padrão não importa.

2. Reverta o tema padrão para o padrão ggplot2. Você pode fazer isso com uma linha de código:

theme_set(theme_gray())

3. Chame as funções do cowplot sem anexar o pacote. Você também não pode chamar library(cowplot)ou, em require(cowplot)vez disso, chamar funções de cowplot anexando cowplot::. Por exemplo, o exemplo acima usando o tema padrão ggplot2 se tornaria:

## 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")

insira a descrição da imagem aqui

Atualizações:

  • No cowplot 1.0, o tema padrão do ggplot2 não é mais alterado.
  • A partir do ggplot2 3.0.0, os gráficos podem ser rotulados diretamente, veja, por exemplo, aqui.
Claus Wilke
fonte
no gráfico de saída está removendo o tema de segundo plano, dos dois gráficos? existe alguma alternativa?
VAR121
@ VAR121 Sim, é uma linha de código. Explicou no final da primeira seção da vinheta de introdução: cran.rstudio.com/web/packages/cowplot/vignettes/...
Claus Wilke
É possível ter a mesma escala y para todas as parcelas com este pacote?
Herman Toothrot
Você precisará definir as escalas y manualmente para corresponder. Ou considere faceta.
21917 Claus Wilke
Você pode definir um ggtitle () para cada plotagem antes de usar grid.arrange ()?
23717 Seanosapien
49

Você pode usar a seguinte multiplotfunção no livro de receitas R de Winston Chang

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

}
David LeBauer
fonte
24

Usando o pacote de retalhos , você pode simplesmente usar o +operador:

library(ggplot2)
library(patchwork)

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


p1 + p2

patchwork

Deena
fonte
Apenas para completar, os retalhos agora também estão no CRAN. Espero que você esteja feliz com minha edição pequena
Tjebo
18

Sim, acho que você precisa organizar seus dados adequadamente. Uma maneira seria a seguinte:

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

Tenho certeza de que existem truques melhores em plyr ou remodelar - ainda não estou realmente atualizado sobre todos esses pacotes poderosos da Hadley.

Dirk Eddelbuettel
fonte
16

Usando o pacote remodelar, você pode fazer algo assim.

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
fonte
12

Há também um pacote multipanelfigure que vale a pena mencionar. Veja também esta resposta .

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

Criado em 06/07/2018 pelo pacote reprex (v0.2.0.9000).

Tung
fonte
9

O ggplot2 é baseado em gráficos de grade, que fornecem um sistema diferente para organizar gráficos em uma página. O par(mfrow...)comando não tem um equivalente direto, pois os objetos de grade (chamados grobs ) não são necessariamente desenhados imediatamente, mas podem ser armazenados e manipulados como objetos R regulares antes de serem convertidos em uma saída gráfica. Isso permite maior flexibilidade do que desenhar agora modelo de gráficos de base, mas a estratégia é necessariamente um pouco diferente.

Eu escrevi grid.arrange()para fornecer uma interface simples o mais próximo possível par(mfrow). Na sua forma mais simples, o código seria semelhante a:

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)

insira a descrição da imagem aqui

Mais opções são detalhadas nesta vinheta .

Uma queixa comum é que as plotagens não estão necessariamente alinhadas, por exemplo, quando elas possuem rótulos de eixo de tamanho diferente, mas isso ocorre por design: grid.arrangenão faz nenhuma tentativa para objetos especiais do caso ggplot2, e os trata igualmente para outros grobs (plotagens de treliça, por exemplo ) Apenas coloca as garras em um layout retangular.

Para o caso especial dos objetos ggplot2, escrevi outra função, ggarrangecom uma interface semelhante, que tenta alinhar os painéis de plotagem (incluindo plotagens facetadas) e tenta respeitar as proporções de aspecto quando definidas pelo usuário.

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

Ambas as funções são compatíveis com ggsave(). Para uma visão geral das diferentes opções e algum contexto histórico, esta vinheta oferece informações adicionais .

baptiste
fonte
9

Atualização: Esta resposta é muito antiga. gridExtra::grid.arrange()agora é a abordagem recomendada. Deixo isso aqui caso seja útil.


Stephen Turner postou a arrange()função no blog Getting Genetics Done (consulte a publicação para obter instruções sobre a aplicação)

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
  }
 }
}
Jeromy Anglim
fonte
9
é basicamente uma versão muito desatualizada de grid.arrange(gostaria de não ter postado nas listas de discussão no momento - não há como atualizar esses recursos on-line), a versão empacotada é uma escolha melhor se você me perguntar
baptiste
4

Usando 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

insira a descrição da imagem aqui

brilhante
fonte
1

As soluções acima podem não ser eficientes se você deseja plotar várias plotagens ggplot usando um loop (por exemplo, conforme solicitado aqui: Criando múltiplas plotagens no ggplot com diferentes valores do eixo Y usando um loop ), que é uma etapa desejada na análise do desconhecido ( ou grandes) conjuntos de dados (por exemplo, quando você deseja plotar contagens de todas as variáveis ​​em um conjunto de dados).

O código abaixo mostra como fazer isso usando o 'multiplot ()' mencionado acima, cuja fonte está aqui: 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)
}

Agora execute a função - para obter Counts para todas as variáveis ​​impressas usando ggplot em uma página

dt = ggplot2::diamonds
plotAllCounts(dt)

Uma coisa a observar é que:
using aes(get(strX)), que você usaria normalmente em loops ao trabalhar ggplot, no código acima, em vez de aes_string(strX)NÃO, desenhará os gráficos desejados. Em vez disso, ele plotará o último gráfico várias vezes. Eu não descobri o porquê - pode ser necessário fazer isso aese aes_stringser chamado ggplot.

Caso contrário, espero que você ache a função útil.

IVIM
fonte
11
Observe que seu código cresce plotsobjetos nos for-loopquais é altamente ineficiente e não é recomendado R. Por favor, veja estes grandes mensagens para descobrir melhores maneiras de fazer isso: acumulação eficiente em R , aplicação de uma função sobre linhas de um quadro de dados e fluxos de trabalho no R Row-orientado com o tidyverse
Tung
Maneira mais eficiente para percorrer variáveis é usar tidy evaluationabordagem que está disponível desde ggplot2 v.3.0.0 stackoverflow.com/a/52045613/786542
Tung
0

Na minha experiência, gridExtra: grid.arrange funciona perfeitamente, se você estiver tentando gerar gráficos em um loop.

Snippet de código curto:

gridExtra::grid.arrange(plot1, plot2, ncol = 2)
Mayank Agrawal
fonte
Como sua resposta melhora a resposta de baptiste de 2 de dezembro de 17 às 4:20? Sua resposta parece ser uma duplicata. Confira o que faz uma resposta aceitável aqui Como responder
Peter
Não consegui dividir o enredo conforme necessário dentro de um loop e, portanto, a sugestão. Inicialmente, escrevi o snippet completo do meu loop for com sua implementação, mas depois decidi contra ele por enquanto. Atualizará o código completo dentro de uma semana ou mais.
Mayank Agrawal
Verifique a resposta aceita por David LeBauer
Peter
Eu tentei fazê-lo usando o pacote cowplot em primeiro lugar, mas não teve êxito. Na minha verificação rápida, ninguém havia mencionado várias soluções de plotagem em um loop for e, portanto, meu comentário. Consulte-me qualquer comentário se estiver errado.
Mayank Agrawal
Se o código na sua resposta incluísse um loop for, seria diferente.
Peter
-3

O cowplotpacote oferece uma ótima maneira de fazer isso, de maneira adequada à publicação.

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

insira a descrição da imagem aqui

tim
fonte
3
Veja também a resposta e a justificativa mais detalhadas dos autores do pacote acima stackoverflow.com/a/31223588/199217
David LeBauer