Các lô bên cạnh với ggplot2


338

Tôi muốn đặt hai lô cạnh nhau bằng cách sử dụng gói ggplot2 , tức là thực hiện tương đương par(mfrow=c(1,2)).

Ví dụ, tôi muốn có hai ô sau đây hiển thị cạnh nhau với cùng một tỷ lệ.

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

Tôi có cần đặt chúng trong cùng một data.frame không?

qplot(displ, hwy, data=mpg, facets = . ~ year) + geom_smooth()

Tôi nghĩ rằng bạn có thể làm điều này với mạng tinh thể. Là ggplot2 là một yêu cầu khó khăn?
JD Long

8
Không. Nhưng tôi đã dành thời gian để điều chỉnh các qplots nên đó là cách tôi thích. :-) Và tôi đang cố gắng chơi xung quanh với ggplot.
Christopher DuBois


1
Để có cái nhìn tổng quan đẹp, hãy xem họa tiết cho gói trứng : Bố trí nhiều ô trên một trang
Henrik

Câu trả lời:


503

Bất kỳ ggplots cạnh nhau (hoặc n lô trên lưới)

Các chức năng grid.arrange()trong gridExtragói sẽ kết hợp nhiều lô; đây là cách bạn đặt hai bên cạnh nhau

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

Điều này hữu ích khi hai ô không dựa trên cùng một dữ liệu, ví dụ nếu bạn muốn vẽ các biến khác nhau mà không sử dụng định hình lại ().

Điều này sẽ vẽ đầu ra như là một hiệu ứng phụ. Để in tác dụng phụ vào một tập tin, xác định một trình điều khiển thiết bị (ví dụ như pdf, png, vv), ví dụ:

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

hoặc, sử dụng arrangeGrob()kết hợp với ggsave(),

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

Điều này tương đương với việc tạo ra hai lô riêng biệt bằng cách sử dụng par(mfrow = c(1,2)). Điều này không chỉ tiết kiệm thời gian sắp xếp dữ liệu, nó là cần thiết khi bạn muốn hai lô khác nhau.


Phụ lục: Sử dụng các khía cạnh

Các khía cạnh là hữu ích để thực hiện các lô tương tự cho các nhóm khác nhau. Điều này được chỉ ra dưới đây trong nhiều câu trả lời dưới đây, nhưng tôi muốn làm nổi bật cách tiếp cận này với các ví dụ tương đương với các ô trên.

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)

Cập nhật

các plot_gridchức năng trong cowplotlà giá trị kiểm tra ra như một thay thế cho grid.arrange. Xem câu trả lời của @ claus-wilke bên dưới và họa tiết này để biết cách tiếp cận tương đương; nhưng chức năng cho phép kiểm soát tốt hơn về vị trí và kích thước lô, dựa trên họa tiết này .


2
Khi tôi chạy mã của bạn bằng cách sử dụng các đối tượng ggplot, sidebysideplot là null. Nếu bạn muốn lưu kết quả đầu ra vào một tệp, hãy sử dụng GridArrange. Xem stackoverflow.com/questions/17059099/ Mạnh
Jim

@Jim cảm ơn bạn đã chỉ ra điều đó. Tôi đã sửa đổi câu trả lời của tôi. Hãy cho tôi biết nếu có bất kỳ câu hỏi vẫn còn.
David LeBauer

1
Là lưới.aarange hiện nay?
Atticus29

?grid.arrangelàm cho tôi nghĩ rằng chức năng này bây giờ được gọi là sortGrob. Tôi đã có thể làm những gì tôi muốn bằng cách làm a <- arrangeGrob(p1, p2)và sau đó print(a).
blakeoft

@blakeoft bạn đã xem các ví dụ chưa? grid.arrangevẫn là một chức năng hợp lệ, không phản đối. Bạn đã thử sử dụng chức năng? Điều gì xảy ra, nếu không phải là những gì bạn mong đợi.
David LeBauer

159

Một nhược điểm của các giải pháp dựa trên grid.arrangelà chúng gây khó khăn cho việc dán nhãn các ô bằng chữ cái (A, B, v.v.), như hầu hết các tạp chí yêu cầu.

Tôi đã viết gói cowplot để giải quyết vấn đề này (và một vài vấn đề khác), cụ thể là chức năng 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")

nhập mô tả hình ảnh ở đây

Đối tượng plot_grid()trả về là một đối tượng ggplot2 khác và bạn có thể lưu nó ggsave()như bình thường:

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

Ngoài ra, bạn có thể sử dụng chức năng cowplot save_plot(), đó là một trình bao bọc mỏng xung quanh ggsave()giúp dễ dàng có được kích thước chính xác cho các ô kết hợp, ví dụ:

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

(Đối ncol = 2số cho biết save_plot()rằng có hai ô cạnh nhau và save_plot()làm cho hình ảnh được lưu rộng gấp đôi.)

Để biết mô tả sâu hơn về cách sắp xếp các ô trong lưới, hãy xem họa tiết này. Ngoài ra còn có một họa tiết giải thích làm thế nào để tạo ra âm mưu với một huyền thoại được chia sẻ.

Một điểm thường gặp của sự nhầm lẫn là gói cowplot thay đổi chủ đề ggplot2 mặc định. Gói này hoạt động theo cách đó bởi vì ban đầu nó được viết cho sử dụng trong phòng thí nghiệm nội bộ và chúng tôi không bao giờ sử dụng chủ đề mặc định. Nếu điều này gây ra vấn đề, bạn có thể sử dụng một trong ba cách tiếp cận sau để khắc phục chúng:

1. Đặt chủ đề theo cách thủ công cho mọi âm mưu. Tôi nghĩ rằng đó là cách thực hành tốt để luôn chỉ định một chủ đề cụ thể cho từng cốt truyện, giống như tôi đã làm + theme_bw()trong ví dụ trên. Nếu bạn chỉ định một chủ đề cụ thể, chủ đề mặc định không thành vấn đề.

2. Hoàn nguyên chủ đề mặc định trở lại mặc định ggplot2. Bạn có thể làm điều này với một dòng mã:

theme_set(theme_gray())

3. Gọi các chức năng cowplot mà không đính kèm gói. Bạn cũng không thể gọi library(cowplot)hoặc require(cowplot)thay vào đó gọi các chức năng cowplot bằng cách trả trước cowplot::. Ví dụ: ví dụ trên sử dụng chủ đề mặc định ggplot2 sẽ trở thành:

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

nhập mô tả hình ảnh ở đây

Cập nhật:

  • Kể từ cowplot 1.0, chủ đề ggplot2 mặc định không bị thay đổi nữa.
  • Kể từ ggplot2 3.0.0, các ô có thể được dán nhãn trực tiếp, xem ví dụ ở đây.

trong cowplot đầu ra là loại bỏ chủ đề nền, của cả hai lô? Có cách nào khác không?
VAR121

@ VAR121 Có, đó là một dòng mã. Giải thích ở cuối phần đầu tiên của họa tiết giới thiệu: cran.rstudio.com/web/packages/cowplot/vignettes/ Lỗi
Claus Wilke

Có thể có cùng thang đo y cho tất cả các lô với gói này không?
Herman Toothrot

Bạn sẽ phải đặt thang đo y theo cách thủ công để khớp. Hoặc xem xét khía cạnh.
Claus Wilke

Bạn có thể đặt ggtitle () cho từng ô trước khi sử dụng Grid.arrange () không?
Seanosapien

49

Bạn có thể sử dụng multiplotchức năng sau từ sách nấu ăn R của 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 ))
    }

}

24

Sử dụng gói chắp vá , bạn chỉ cần sử dụng +toán tử:

library(ggplot2)
library(patchwork)

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


p1 + p2

chắp vá


Chỉ để hoàn thiện, chắp vá bây giờ cũng có trên CRAN. Hy vọng bạn hài lòng với bản chỉnh sửa nhỏ của mình
Tjebo

18

Có, methinks bạn cần sắp xếp dữ liệu của bạn một cách thích hợp. Một cách sẽ là thế này:

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

Tôi chắc chắn có những thủ thuật tốt hơn trong plyr hoặc định hình lại - tôi vẫn chưa thực sự tăng tốc trên tất cả các gói mạnh mẽ này của Hadley.


16

Sử dụng gói định hình lại bạn có thể làm một cái gì đó như thế này.

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)

12

Ngoài ra còn có gói đa nhân vật đáng để đề cập. Xem thêm câu trả lời này .

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

Được tạo vào ngày 2018-07-06 bởi gói reprex (v0.2.0.9000).


9

ggplot2 dựa trên đồ họa lưới, cung cấp một hệ thống khác để sắp xếp các ô trên một trang. Các par(mfrow...)lệnh không có tương đương trực tiếp, như các đối tượng lưới (gọi tắt là grobs ) không nhất thiết phải rút ra ngay lập tức, nhưng có thể được lưu trữ và thao tác như các đối tượng R thường xuyên trước khi được chuyển đổi sang một sản lượng đồ họa. Điều này cho phép linh hoạt hơn so với mô hình đồ họa cơ bản hiện nay , nhưng chiến lược này nhất thiết phải có một chút khác biệt.

Tôi đã viết grid.arrange()để cung cấp một giao diện đơn giản gần nhất có thể par(mfrow). Ở dạng đơn giản nhất, mã sẽ trông như sau:

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)

nhập mô tả hình ảnh ở đây

Nhiều tùy chọn được chi tiết trong họa tiết này .

Một khiếu nại phổ biến là các ô không nhất thiết phải được căn chỉnh, ví dụ như khi chúng có nhãn trục có kích thước khác nhau, nhưng đây là do thiết kế: grid.arrangekhông cố gắng đối với các đối tượng ggplot2 trong trường hợp đặc biệt và xử lý chúng như nhau đối với các rãnh khác (ví dụ như các ô mạng ). Nó chỉ đơn thuần là đặt các rãnh trong một bố cục hình chữ nhật.

Đối với trường hợp đặc biệt của các đối tượng ggplot2, tôi đã viết một hàm khác ggarrange, với giao diện tương tự, cố gắng căn chỉnh các ô vẽ đồ thị (bao gồm các ô được tô màu) và cố gắng tôn trọng các tỷ lệ khung hình khi được xác định bởi người dùng.

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

Cả hai chức năng đều tương thích với ggsave(). Để có cái nhìn tổng quan về các tùy chọn khác nhau và một số bối cảnh lịch sử, họa tiết này cung cấp thêm thông tin .


9

Cập nhật: Câu trả lời này rất cũ. gridExtra::grid.arrange()bây giờ là phương pháp được đề nghị. Tôi để nó ở đây trong trường hợp nó có thể hữu ích.


Stephen Turner đã đăng arrange()chức năng trên blog Bắt gen di truyền (xem bài đăng để biết hướng dẫn ứng dụng)

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

9
về cơ bản nó là một phiên bản rất lỗi thời grid.arrange(ước gì tôi đã không đăng nó trên danh sách gửi thư vào thời điểm đó - không có cách nào để cập nhật các tài nguyên trực tuyến này), phiên bản đóng gói là lựa chọn tốt hơn nếu bạn hỏi tôi
baptiste

4

Sử dụng 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

nhập mô tả hình ảnh ở đây


1

Các giải pháp trên có thể không hiệu quả nếu bạn muốn vẽ nhiều ô ggplot bằng một vòng lặp (ví dụ như được hỏi ở đây: Tạo nhiều ô trong ggplot với các giá trị trục Y khác nhau bằng vòng lặp ), đây là bước mong muốn trong việc phân tích ẩn số ( hoặc lớn) tập dữ liệu (ví dụ: khi bạn muốn vẽ biểu đồ Số lượng của tất cả các biến trong tập dữ liệu).

Đoạn mã dưới đây cho thấy cách thực hiện điều đó bằng cách sử dụng 'Multipot ()' đã đề cập ở trên, nguồn của nó ở đây: http://www.cookbook-r.com/Graphs/Mult Môn_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)
}

Bây giờ hãy chạy hàm - để lấy Đếm cho tất cả các biến được in bằng ggplot trên một trang

dt = ggplot2::diamonds
plotAllCounts(dt)

Một điều cần lưu ý là:
sử dụng aes(get(strX)), mà bạn thường sử dụng trong các vòng lặp khi làm việc với ggplot, trong đoạn mã trên thay vì aes_string(strX)sẽ KHÔNG vẽ các ô mong muốn. Thay vào đó, nó sẽ vẽ cốt truyện cuối cùng nhiều lần. Tôi không hiểu tại sao - nó có thể phải làm aesaes_stringđược gọi vào ggplot.

Nếu không, hy vọng bạn sẽ tìm thấy chức năng hữu ích.


1
Lưu ý rằng mã của bạn phát triển plotscác đối tượng trong for-loopđó rất kém hiệu quả và không được đề xuất R. Vui lòng xem các bài đăng tuyệt vời này để tìm ra cách tốt hơn để thực hiện: Tích lũy hiệu quả trong R , Áp dụng chức năng trên các hàng của khung dữ liệu & quy trình làm việc theo định hướng trong R với tidyverse
Tung

Cách hiệu quả hơn để lặp qua các biến là sử dụng tidy evaluationphương pháp đã có sẵn từ ggplot2 v.3.0.0 stackoverflow.com/a/52045613/786542
Tung

0

Theo kinh nghiệm của tôi GridExtra: Grid.arrange hoạt động hoàn hảo, nếu bạn đang cố gắng tạo các ô trong một vòng lặp.

Đoạn mã ngắn:

gridExtra::grid.arrange(plot1, plot2, ncol = 2)

Làm thế nào để câu trả lời của bạn cải thiện về câu trả lời của baptiste vào ngày 2 tháng 12 năm 17 lúc 4:20? Câu trả lời của bạn dường như là một bản sao. Kiểm tra những gì làm cho một câu trả lời chấp nhận được ở đây Cách trả lời
Peter

Tôi không thể phân chia cốt truyện khi cần trong một vòng lặp, và do đó gợi ý. Ban đầu, tôi đã viết đoạn trích đầy đủ cho vòng lặp for của mình với việc thực hiện nó nhưng sau đó đã quyết định chống lại nó trong thời điểm hiện tại. Sẽ cập nhật mã đầy đủ trong một tuần hoặc lâu hơn.
Mayank Agrawal

Kiểm tra câu trả lời được chấp nhận bởi David LeBauer
Peter

Tôi đã thử nó để làm điều đó bằng cách sử dụng gói cowplot ngay từ đầu nhưng không thành công. Trong quá trình quét nhanh của tôi, không ai đề cập đến nhiều giải pháp vẽ trong vòng lặp for và do đó tôi nhận xét. Giới thiệu cho tôi bất kỳ bình luận nếu tôi sai.
Mayank Agrawal

Nếu mã trong câu trả lời của bạn bao gồm một vòng lặp for sẽ khác.
Peter

-3

Các cowplotgói cung cấp cho bạn một cách tốt đẹp để làm được điều này, theo cách phù hợp với công bố.

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

nhập mô tả hình ảnh ở đây


3
Xem thêm câu trả lời và lý do chi tiết hơn của tác giả gói trên stackoverflow.com/a/31223588/199217
David LeBauer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.