рекомендации

вторник, 23 июля 2019 г.

Простой способ размещения нескольких графиков на одной странице в ggplot2

Перевод. Оригинал здесь.

Для размещения нескольких графиков ggplot2 на одной странице не получится использовать стандартные функции R - par() и layout(). Для этого необходимо задействовать функции grid.arrange() [в пакете gridExtra] и plot_grid() [в пакете cowplot]

Установка и загрузка требуемых пакетов

Установка и загрузка пакета gridExtra

install.packages("gridExtra")
library("gridExtra")

Установка и загрузка пакета cowplot

cowplot можно установить следующим образом:

install.packages("cowplot")

или

с помощью пакета devtools (в таком случае сначала необходимо установить devtools):

devtools::install_github("wilkelab/cowplot")

Загрузка cowplot:

library("cowplot")

Подготовка данных

Будет использоваться набор данных ToothGrowth:

df = ToothGrowth
# Конвертируем численную переменную dose в факторную
df$dose = as.factor(df$dose)
head(df)

## len supp dose
## 1 4.2 VC 0.5
## 2 11.5 VC 0.5
## 3 7.3 VC 0.5
## 4 5.8 VC 0.5
## 5 6.4 VC 0.5
## 6 10.0 VC 0.5

Cowplot: готовые к публикации графики

Пакет cowplot  - это расширение ggplot2, которое используется для создания графиков полиграфического качества.

Базовые графики

library(cowplot)
# График по умолчанию
bp = ggplot(df, aes(x=dose, y=len, color=dose)) +
geom_boxplot() +
theme(legend.position = "none")
bp

# Добавляем линии сетки
bp + background_grid(major = "xy", minor = "none")



Функция ggsave() [из пакета ggplot2] может использоваться для сохранения графиков ggplot. Однако при работе с  cowplot, предпочтительно использовать функцию save_plot() [из пакета cowplot]. Это альтернатива ggsave с лучшей поддержкой составных графиков.

save_plot("mpg.pdf", bp,
base_aspect_ratio = 1.3 # make room for figure legend
)

Организуем несколько графиков с помощью cowplot

# Точечный график
sp = ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl)))+
geom_point(size=2.5)
sp

# Гистограмма
bp = ggplot(diamonds, aes(clarity, fill = cut)) +
geom_bar() +
theme(axis.text.x = element_text(angle=70, vjust=0.5))
bp



Комбинируем эти два графика:

plot_grid(sp, bp, labels=c("A", "B"), ncol = 2, nrow = 1)



Функция draw_plot() может использоваться для размещения графиков в заданном месте с заданным размером. Синтаксис функции:

draw_plot(plot, x = 0, y = 0, width = 1, height = 1)

plot: график для размещения (ggplot2 или gtable).
x: координата x левого нижнего угла графика.
y: координата y левого нижнего угла графика.
width, height: ширина и высота графика.

Функция ggdraw() используется для инициализации пустого холста.

plot.iris = ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point() + facet_grid(. ~ Species) + stat_smooth(method = "lm") +
background_grid(major = 'y', minor = "none") + # добавляем тонкие горизонтальные линии
panel_border() # и границу вокруг каждой панели
# plot.mpt и plot.diamonds были определены ранее
ggdraw() +
draw_plot(plot.iris, 0, .5, 1, .5) +
draw_plot(sp, 0, 0, .5, .5) +
draw_plot(bp, .5, 0, .5, .5) +
draw_plot_label(c("A", "B", "C"), c(0, 0, 0.5), c(1, 0.5, 0.5), size = 15)



grid.arrange: создание и расположение нескольких графиков

Код R ниже создает ящик к усами (box plot), точечный график (dot plot), скрипичную диаграмму (violin plot) и точечную диаграмму рассеивания (strip chart):

library(ggplot2)
# создание box plot
bp = ggplot(df, aes(x=dose, y=len, color=dose)) +
geom_boxplot() +
theme(legend.position = "none")

# создание dot plot
# добавление точки данных и стандартного отклонения
dp = ggplot(df, aes(x=dose, y=len, fill=dose)) +
geom_dotplot(binaxis='y', stackdir='center')+
stat_summary(fun.data=mean_sdl, mult=1,
geom="pointrange", color="red")+
theme(legend.position = "none")

# создание violin plot
vp = ggplot(df, aes(x=dose, y=len)) +
geom_violin()+
geom_boxplot(width=0.1)

# создание stripchart
sc = ggplot(df, aes(x=dose, y=len, color=dose, shape=dose)) +
geom_jitter(position=position_jitter(0.2))+
theme(legend.position = "none") +
theme_gray()

Комбинируем графики, используя функцию grid.arrange() [в gridExtra]:

library(gridExtra)
grid.arrange(bp, dp, vp, sc, ncol=2, nrow =2)



grid.arrange() и arrangeGrob(): меняем ширину столбца/строки графика

Код R ниже:

box plot будет размещен в первом столбце.
dot plot и strip chart будут во втором столбце.

grid.arrange(bp, arrangeGrob(dp, sc), ncol = 2)



Также можно использовать аргумент layout_matrix в grid.arrange(). В коде R ниже layout_matrix - это матрица 2X3 (2 столбца и 3 строки). В первом столбце, где все единицы, расположен первый график и он занимает три строки, второй столбец включает графики 2, 3, 4, каждый занимает одну строку.

grid.arrange(bp, dp, sc, vp, ncol = 2,
layout_matrix = cbind(c(1,1,1), c(2,3,4)))



Добавление общей легенды к нескольким графикам ggplot2

Это можно сделать за четыре простых шага:

1. Создаем графики: p1, p2, ….
2. Сохраняем легенду для графика p1 как внешний графический элемент (называемый “grob” в терминологии Grid).
3. Удаляем легенды со всех графиков.
4. Рисуем все графики только с одной легендой в правой панели.

Для сохранения легенды в ggplot может использоваться следующая функция:

library(gridExtra)
get_legend = function(myggplot){
tmp = ggplot_gtable(ggplot_build(myggplot))
leg = which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- span=""> tmp$grobs[[leg]]
return(legend)
}

Приведенная функция взята с этого форума

# 1. Создаем графики
#++++++++++++++++++++++++++++++++++
# Создаем box plot
bp = ggplot(df, aes(x=dose, y=len, color=dose)) +
geom_boxplot()

# Создаем violin plot
vp = ggplot(df, aes(x=dose, y=len, color=dose)) +
geom_violin()+
geom_boxplot(width=0.1)+
theme(legend.position="none")

# 2. Сохраняем легенду
#+++++++++++++++++++++++
legend = get_legend(bp)

# 3.Удаляем легенду с box plot
#+++++++++++++++++++++++
bp = bp + theme(legend.position="none")

# 4. Размещаем графики ggplot2 с заданной шириной
grid.arrange(bp, vp, legend, ncol=3, widths=c(2.3, 2.3, 0.8))



Изменение позиции легенды

# 1. Создаем графики
#++++++++++++++++++++++++++++++++++
# Создаем box plot с легендой сверху
bp = ggplot(df, aes(x=dose, y=len, color=dose)) +
geom_boxplot()+theme(legend.position = "top")

# Создаем violin plot
vp = ggplot(df, aes(x=dose, y=len, color=dose)) +
geom_violin()+
geom_boxplot(width=0.1)+
theme(legend.position="none")

# 2. Сохраняемлегенду
#+++++++++++++++++++++++
legend = get_legend(bp)

# 3. Удаляем легенду с box plot
#+++++++++++++++++++++++
bp = bp + theme(legend.position="none")

# 4. Создаем пустой график
blankPlot = ggplot()+geom_blank(aes(1,1)) +
cowplot::theme_nothing()

Изменение позиции легенды путем изменения порядка графиков. Создаются решетки из четырех ячеек (2х2). Высота зоны легенды задается 0.2

Легенда сверху слева:



# Легенда сверху слева
grid.arrange(legend, blankPlot, bp, vp,
ncol=2, nrow = 2,
widths = c(2.7, 2.7), heights = c(0.2, 2.5))



Легенда сверху справа:



# Сверху справа
grid.arrange(blankPlot, legend, bp, vp,
ncol=2, nrow = 2,
widths = c(2.7, 2.7), heights = c(0.2, 2.5))



Легенда снизу слева и снизу справа может быть построена следующим образом:

# Легенда снизу слева
grid.arrange(bp, vp, legend, blankPlot,
ncol=2, nrow = 2,
widths = c(2.7, 2.7), heights = c(2.5, 0.2))
# Снизу справа
grid.arrange( bp, vp, blankPlot, legend,
ncol=2, nrow = 2,
widths = c(2.7, 2.7), heights = c( 2.5, 0.2))

Также можно использовать аргумент layout_matrix для выбора позиции легенды. В коде R ниже  layout_matrix - это матрица 2X2:
- первая строка (height = 2.5) указывает на расположение первого (bp) и второго графика (vp).
- во второй строке (height = 0.2) в двух столбцах помещается легенда.

Легенда снизу по центру:

grid.arrange(bp, vp, legend, ncol=2, nrow = 2,
layout_matrix = rbind(c(1,2), c(3,3)),
widths = c(2.7, 2.7), heights = c(2.5, 0.2))



Легенда сверху по центру:

- легенда (plot 1) в первой строке (height = 0.2), занимает два столбца
- bp (plot 2) и vp (plot 3) во второй строке (height = 2.5)

grid.arrange(legend, bp, vp, ncol=2, nrow = 2,
layout_matrix = rbind(c(1,1), c(2,3)),
widths = c(2.7, 2.7), heights = c(0.2, 2.5))



График рассеяния с гистограммами плотностей

Шаг 1/3. Создадим данные:

set.seed(1234)
x = c(rnorm(500, mean = -1), rnorm(500, mean = 1.5))
y = c(rnorm(500, mean = 1), rnorm(500, mean = 1.7))
group = as.factor(rep(c(1,2), each=500))
df2 = data.frame(x, y, group)
head(df2)

x y group 1 -2.20706575 -0.2053334 1 2 -0.72257076 1.3014667 1 3 0.08444118 -0.5391452 1 4 -3.34569770 1.6353707 1 5 -0.57087531 1.7029518 1 6 -0.49394411 -0.9058829 1

Шаг 2/3. Создадим графики:

# График рассеяния переменных x и y с цветами по переменной group
scatterPlot = ggplot(df2,aes(x, y, color=group)) +
geom_point() +
scale_color_manual(values = c('#999999','#E69F00')) +
theme(legend.position=c(0,1), legend.justification=c(0,1))

# Гистограмма плотностей x (верхняя панель)
xdensity = ggplot(df2, aes(x, fill=group)) +
geom_density(alpha=.5) +
scale_fill_manual(values = c('#999999','#E69F00')) +
theme(legend.position = "none")

# Гистограмма плотностей y (правая панель)
ydensity = ggplot(df2, aes(y, fill=group)) +
geom_density(alpha=.5) +
scale_fill_manual(values = c('#999999','#E69F00')) +
theme(legend.position = "none")

Создаем пустой график:

blankPlot = ggplot()+geom_blank(aes(1,1))+
theme(
plot.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.line = element_blank()
)

Шаг 3/3. Соединяем графики вместе:

Размещаем ggplot2 с адаптированной высотой и шириной каждого столбца:

library("gridExtra")
grid.arrange(xdensity, blankPlot, scatterPlot, ydensity,
ncol=2, nrow=2, widths=c(4, 1.4), heights=c(1.4, 4))



Создание сложной разметки с помощью функции viewport()

Отличающиеся шаги:

1. Создаем графики : p1, p2, p3, ….
2. Переходим на новую страницу с помощью функции grid.newpage()
3. Создаем разметку 2X2 - number of columns = 2; number of rows = 2
4. Определим область построения графиков: прямоугольная область на графическом устройстве
5. Выводим график в области построения

# Переходим на новую страницу
grid.newpage()

# Создаем разметку: nrow = 2, ncol = 2
pushViewport(viewport(layout = grid.layout(2, 2)))

# вспомогательная функция для определения области разметки
define_region = function(row, col){
viewport(layout.pos.row = row, layout.pos.col = col)
}

# Размещаем графики
print(scatterPlot, vp=define_region(1, 1:2))
print(xdensity, vp = define_region(2, 1))
print(ydensity, vp = define_region(2, 2))



ggExtra: добавляем гистограммы распределения плотностей на графики рассеяния ggplot2

Пакет ggExtra - это простой в использовании пакет, разработанный Dean Attali, для добавления маргинальных гистограмм,  boxplot или кривых распределения плотностей на графики рассеяния ggplot2.

Пакет устанавливается и используется следующим образом:

# Установка
install.packages("ggExtra")

# Загрузка
library("ggExtra")

# Создаем набор данных
set.seed(1234)
x = c(rnorm(500, mean = -1), rnorm(500, mean = 1.5))
y = c(rnorm(500, mean = 1), rnorm(500, mean = 1.7))
df3 = data.frame(x, y)

# График рассеяния для переменных x и y
sp2 = ggplot(df3,aes(x, y)) + geom_point()

# Кривые распределения плотностей
ggMarginal(sp2 + theme_gray())



# Маргинальные гистограммы
ggMarginal(sp2 + theme_gray(), type = "histogram",
fill = "steelblue", col = "darkblue")



Вставка в ggplot внешних графических элементов

Функция annotation_custom() [in ggplot2] может быть использована для добавления таблиц, графиков или других элементов. Упрощенный формат ее:

annotation_custom(grob, xmin, xmax, ymin, ymax)

grob: внешний графический элемент
xmin, xmax:  расположение в сетке координат по оси x 
ymin, ymax: расположение в сетке координат по оси y

Отличающиеся шаги :
1. Создаем график рассеяния для y = f(x)
2. Добавляем, например,box plot переменных x и y внутри графика рассеяния, используя функцию annotation_custom()
Если вставленный box plot накладывается на некоторые точки графика, используем для него прозрачный фон  (transparent background)

# Создаем объект прозрачной темы
transparent_theme <- span=""> theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
panel.grid = element_blank(),
axis.line = element_blank(),
panel.background = element_rect(fill = "transparent",colour = NA),
plot.background = element_rect(fill = "transparent",colour = NA))

Создаем графики:

p1 = scatterPlot # см. предыдущий раздел для scatterPlot

# Box plot переменной x
p2 = ggplot(df2, aes(factor(1), x))+
geom_boxplot(width=0.3)+coord_flip()+
transparent_theme

# Box plot переменной y
p3 = ggplot(df2, aes(factor(1), y))+
geom_boxplot(width=0.3)+
transparent_theme

# Создаем внешние графические элементы
# вызываем "grop" в терминологии Grid
p2_grob = ggplotGrob(p2)
p3_grob = ggplotGrob(p3)

# вставляем p2_grob в scatter plot
xmin = min(x); xmax <- span=""> max(x)
ymin = min(y); ymax <- span=""> max(y)
p1 + annotation_custom(grob = p2_grob, xmin = xmin, xmax = xmax,
ymin = ymin-1.5, ymax = ymin+1.5)



# Вставляем p3_grob в scatter plot
p1 + annotation_custom(grob = p3_grob,
xmin = xmin-1.5, xmax = xmin+1.5,
ymin = ymin, ymax = ymax)



Комбинируем таблицу, текст и графики ggplot2 

Требуются указанные ниже функции:
tableGrob() [в пакете gridExtra]: для добавления таблицы с данными к графическому устройству
splitTextGrob() [в пакете RGraphics]: для добавления текста к графику

Убедитесь, что пакет RGraphics установлен.

library(RGraphics)
library(gridExtra)

# Таблица
p1 = tableGrob(head(ToothGrowth))

# Текст
text = "ToothGrowth data describes the effect of Vitamin C on tooth growth in Guinea pigs. Three dose levels of Vitamin C (0.5, 1, and 2 mg) with each of two delivery methods [orange juice (OJ) or ascorbic acid (VC)] are used."
p2 = splitTextGrob(text)

# Box plot
p3 = ggplot(df, aes(x=dose, y=len)) + geom_boxplot()

# Размещаем графики на одной странице
grid.arrange(p1, p2, p3, ncol=1)



Комментариев нет:

Отправить комментарий