Как расположить участки с общими осями?
Я пытаюсь создать расположение из трех диаграмм рассеяния с общими осями и маргинальными гистограммами. Кажется, это должно быть просто, но это дает мне приступы. Я пробовал подходы с gridExtra и gtable, оба получили общее расположение, как я хочу, но с выравниванием и размерами графика, которые были отключены.
Есть много других постов, связанных с этим вопросом, и я экспериментировал с ответами на многие из них, особенно с ответами @baptiste здесь и здесь. Подход последнего к контролю ширины может быть ключом к выравниванию, но я недостаточно хорошо его понял, чтобы приспособить его к моей проблеме.
Ниже приведен минимальный рабочий пример, которому предшествует его результат. Результат имеет довольно много проблем:
- Нижний правый участок больше двух других. Это было проблемой для всех подходов, включая grid.arrange(), с помощью которого я поместил пустой прямоугольный гроб, чтобы сбалансировать графики рассеяния.
- Размеры мартинговых гистограмм далеко не такие, как я ожидал бы из кода, так как их узкий размер был бы примерно 1/4 размера ширины диаграмм рассеяния.
- Расстояние между участками слишком велико.
- Ни одна из маргинальных гистограмм не совмещается правильно с графиками рассеяния.
- Гистограмма на полях справа (c) настолько узка, что вообще не отображается в png, но все же она отображается (хотя и слишком узкая) в средстве просмотра графиков RStudio, хотя и только в режиме "масштабирования".
Бонусные баллы за ответ, который правильно выровняет оси графиков, даже если ширина меток осей варьируется между графиками, так как это будет гораздо более адаптируемым для будущих проблем.
library(ggplot2)
library(grid)
library(gtable)
data <- data.frame(a = rnorm(100, 30, 3), b = rnorm(100, 40, 5), c=rnorm(100, 50, 3))
b_a.scatter <- ggplot(data, aes(x=a, y=b)) + geom_point() + coord_equal(ratio=1, xlim=c(0,100), ylim=c(0,100)) +
theme(
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
plot.margin = unit(c(1,0,-0.5,1), "cm")
)
c_a.scatter <- ggplot(data, aes(x=a, y=c)) + geom_point() + coord_equal(ratio=1, xlim=c(0,100), ylim=c(0,100)) +
theme(plot.margin = unit(c(-0.5,0,0.5,1), "cm"))
c_b.scatter <- ggplot(data, aes(x=b, y=c)) + geom_point() + coord_equal(ratio=1, xlim=c(0,100), ylim=c(0,100)) +
theme(
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(-0.5,0,0.5,0), "cm")
)
a.hist <- ggplot(data, aes(x=a)) + geom_histogram() + coord_equal(xlim=c(0,100), ratio=1/4) +
theme(
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(0,0,1,1), "cm")
)
b.hist <- ggplot(data, aes(x=b)) + geom_histogram() + coord_equal(xlim=c(0,100), ratio=1/4) +
theme(
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(0,0,1,0), "cm")
)
c.hist <- ggplot(data, aes(x=c)) + geom_histogram() + coord_flip(xlim=c(0,100)) +
theme(
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
plot.margin = unit(c(0,1,0,0), "cm")
)
blankPanel <- grid.rect(gp=gpar(col="white"))
gt <- gtable(widths = unit(rep(1,9), "null"),
heights = unit(rep(1,9), "null"),
respect=T)
gl <- list(ggplotGrob(b_a.scatter),
ggplotGrob(c_a.scatter), ggplotGrob(c_b.scatter), ggplotGrob(c.hist),
ggplotGrob(a.hist), ggplotGrob(b.hist))
gt <- gtable_add_grob(gt, gl,
l=c(1,1,5,9,1,5),
r=c(4,4,8,9,4,8),
t=c(1,5,5,5,9,9),
b=c(4,8,8,8,9,9))
grid.newpage()
png('multiplot.png')
grid.draw(gt)
dev.off()
1 ответ
Вот один из подходов: объединить графы столбец за столбцом, используя rbind, затем cbind трех столбцов. Фиктивные таблицы представлены для пустых ячеек, они содержат только информацию о макете.
library(ggplot2)
library(gtable)
d <- data.frame(a = rnorm(100, 30, 3),
b = rnorm(100, 40, 5),
c=rnorm(100, 50, 3))
theme_set(theme_bw() +
theme(plot.background=element_rect(colour="red",size = 2)))
## define simpler plots
a <- ggplot(d, aes(x=a, y=b)) + geom_point() + xlim(0,100)+ ylim(0,90)
b <- ggplot(d, aes(x=a, y=c)) + geom_point() + xlim(0,100) + ylim(0,90)
c <- ggplot(d, aes(x=b, y=c)) + geom_point() + xlim(0,100) + ylim(0,90)
ah <- ggplot(d, aes(x=a)) + geom_histogram() +
xlim(0,100)+ ylim(0,2000000)
bh <- ggplot(d, aes(x=b)) + geom_histogram() +
xlim(0,100) + ylim(0,2000000)
ch <- ggplot(d, aes(x=c)) + geom_histogram() +
coord_flip(xlim=c(0,200000))
pl <- lapply(list(a,b,c,ah,bh,ch), ggplotGrob)
## function to create a dummy table (no grobs, zero size) of the right dim for (r/c)bind
dummy_gtable <- function(g){
gtable(widths=unit(rep(0,ncol(g)), 'null'), heights=unit(rep(0, nrow(g)), 'null'))
}
left <- rbind(pl[[1]],pl[[2]],pl[[4]])
middle <- rbind(dummy_gtable(pl[[1]]),pl[[3]],pl[[5]])
right <- rbind(dummy_gtable(pl[[1]]),pl[[6]], dummy_gtable(pl[[5]]))
grid.newpage()
grid.draw(cbind(left, middle, right))
Обратите внимание, что я использую cbind
а также rbind
из моего экспериментального форка gtable, потому что выпущенная версия не использует unit.pmax
как единица сравнения для ширины и высоты. Пользовательские функции можно позаимствовать из другого вопроса, чтобы продолжать использовать стабильную версию gtable.