Побічні сюжети з ggplot2


339

Я хотів би розмістити два сюжети поруч, використовуючи пакет 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()

Я думаю, ви могли б зробити це за допомогою решітки. Чи є ggplot2 важка вимога?
JD Long

8
Ні. Але я вже поставив час, коли виправляв qplots, тому це було саме те, що мені сподобалось. :-) І я намагаюся пограти з ggplot.
Крістофер Дюбуа

1
Ознайомтеся з stackoverflow.com/questions/31798162/…
Boern

1
Для приємного огляду дивіться віньєтку для пакету яєць : Розкладка кількох сюжетів на сторінці
Генрік,

Відповіді:


505

Будь-які ggplots поруч (або n ділянок у сітці)

Функція grid.arrange()в gridExtraпакеті буде поєднувати кілька сюжетів; ось так ви ставите два поруч.

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

Це корисно, коли два графіки не базуються на одних і тих же даних, наприклад, якщо ви хочете побудувати різні змінні без використання переформатування ().

Це створить вихід як побічний ефект. Щоб надрукувати побічний ефект в файл, вказати драйвер пристрою (наприклад 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 нижче та цю віньєтку для еквівалентного підходу; але функція дозволяє чіткіше керувати розташуванням та розміром сюжету на основі цієї віньєтки .


2
Коли я запускав ваш код за допомогою ggplot-об'єктів, sidebysideplot є нульовим. Якщо ви хочете зберегти вихід у файл, використовуйте gridArrange. Дивіться stackoverflow.com/questions/17059099/…
Джим

@ Джим дякую, що вказав на це. Я переглянув свою відповідь. Повідомте мене, якщо залишилися запитання.
David LeBauer

1
Чи вимкнено grid.aarange зараз?
Atticus29

?grid.arrangeзмушує мене думати, що тепер ця функція називається sitar. Я міг робити те, що хотів, роблячи a <- arrangeGrob(p1, p2)і тоді print(a).
blakeoft

@blakeoft Ви подивилися приклади? grid.arrangeвсе ще є дійсною, непридатною функцією. Ви намагалися використовувати функцію? Що станеться, якщо не те, що ви очікували.
Девід ЛеБуер

159

Одним із недоліків рішень, заснованих на grid.arrangeтому, що вони ускладнюють маркування сюжетів літерами (A, B тощо), як цього вимагає більшість журналів.

Я написав пакет ковпаків, щоб вирішити цю (та кілька інших) проблем, зокрема функцію 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)

Крім того, ви можете скористатися функцією коров'ячої труби 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. Зателефонуйте до функцій ковзани, не додаючи пакет. Ви також не можете зателефонувати library(cowplot)або require(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")

введіть тут опис зображення

Оновлення:

  • Як і для cowplot 1.0, тема ggplot2 за замовчуванням більше не змінюється.
  • За станом на ggplot2 3.0.0, ділянки можна позначати безпосередньо, див. Наприклад, тут.

у вихідному ковпаку видалення фонової теми обох сюжетів? чи є альтернатива?
VAR121

@ VAR121 Так, це один рядок коду. Пояснено в кінці першого розділу вступної віньєтки: cran.rstudio.com/web/packages/cowplot/vignettes/…
Клаус Вільке

Чи можливо мати однаковий шкала у всіх ділянках за допомогою цього пакету?
Зубний Герман

Вам доведеться встановити y шкала вручну, щоб відповідати. Або розглянути граніт.
Клаус Вільке

Ви можете встановити ggtitle () для кожного сюжету, перш ніж використовувати grid.arrange (), хоча?
Seanosapien

49

Ви можете використовувати наступну multiplotфункцію з кулінарної книги Вінстона Чанга

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

Використовуючи печворковий пакет, ви можете просто скористатися +оператором:

library(ggplot2)
library(patchwork)

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


p1 + p2

печворк


Для повноти, клаптики тепер також є на CRAN. Сподіваюся, ви задоволені моєю невеликою
редакцією

18

Так, потрібні вам упорядковані дані. Один із способів:

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 або переформатуванні - я все ще не дуже швидко на всіх цих потужних пакетах від Hadley.


16

Використовуючи пакет змін, ви можете зробити щось подібне.

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

Існує також багатопанельний пакет, який варто згадати. Дивіться також цю відповідь .

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 пакетом reprex (v0.2.0.9000).


9

ggplot2 заснований на сітковій графіці, яка забезпечує іншу систему впорядкування сюжетів на сторінці. Thepar(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(). Для загального огляду різних варіантів та деякого історичного контексту ця віньєтка пропонує додаткову інформацію .


9

Оновлення: Ця відповідь дуже стара. 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
  }
 }
}

9
це в основному застаріла версія grid.arrange(хотілося б, щоб я не розміщував її в списках розсилки в той час - немає можливості оновлювати ці інтернет-ресурси), упакована версія - кращий вибір, якщо ви запитаєте мене
baptiste

4

Використання 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

введіть тут опис зображення


1

Вищезазначені рішення можуть бути неефективними, якщо ви хочете побудувати кілька сюжетів 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)
}

Тепер запустіть функцію - отримати підрахунки для всіх змінних, надрукованих за допомогою ggplot на одній сторінці

dt = ggplot2::diamonds
plotAllCounts(dt)

Варто зазначити, що:
використання aes(get(strX)), яке ви зазвичай використовуєте в циклі під час роботи ggplot, у наведеному вище коді замість aes_string(strX)НЕ намалює потрібні ділянки. Натомість останній сюжет будується багато разів. Я не зрозумів, чому - можливо, це доведеться робити aesі aes_stringвикликаються ggplot.

В іншому випадку сподіваємось, що ця функція стане корисною.


1
Зауважте, що ваш код збільшує plotsоб'єкти, в for-loopяких дуже неефективний і не рекомендується R. Будь ласка, ознайомтеся з цими чудовими повідомленнями, щоб дізнатися про кращі способи цього: Ефективне накопичення в R , Застосування функції над рядками фрейму даних та робочих потоків, орієнтованих на рядки, в R з tidyverse
Tung

Більш ефективним способом перегляду змінних є використання tidy evaluationпідходу, який доступний з моменту ggplot2 v.3.0.0 stackoverflow.com/a/52045613/786542
Tung

0

На мій досвід gridExtra: grid.arrange прекрасно працює, якщо ви намагаєтеся генерувати ділянки в циклі.

Код короткого коду:

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

Як покращується ваша відповідь на відповідь баптиста 2 грудня 177 о 4:20? Ваша відповідь здається дублікатом. Перевірте, що робить прийнятною відповідь тут. Як відповісти
Петро

Я не зміг розділити сюжет за потребою в циклі, а отже, і пропозиція. Спочатку я написав повний фрагмент моєї програми для циклу з її реалізацією, але потім вирішив проти цього. Оновить повний код через тиждень або близько того.
Mayank Agrawal

Я спробував це зробити за допомогою пакету коров'ячої сировини в першу чергу сам, але не вдався. Під час мого швидкого сканування ніхто не згадав про багаторазове вирішення схеми в циклі for for, а отже, і мій коментар. Надішліть мені будь-який коментар, якщо я помиляюся.
Agrawal Mayank

Якби код у вашій відповіді містив цикл for, який би відрізнявся.
Петро

Я б оновив його через тижневі проби тут і весь проект на Kaggle. Ура.
Майка Агравал

-3

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

введіть тут опис зображення


3
Дивіться також більш докладну відповідь авторів пакета і логічне обґрунтування вище stackoverflow.com/a/31223588/199217
Девід LeBauer
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.