R, ggplot - графики, разделяющие одну и ту же ось y, но с различными шкалами по оси x

Контекст

У меня есть несколько наборов данных/переменных, и я хочу их построить, но я хочу сделать это в компактном виде. Для этого я хочу, чтобы они делили одну и ту же ось Y, но отличались по оси X, и из-за разных распределений я хочу, чтобы одна из осей оси была масштабирована в журнале, а другая линейная масштабировалась.

Пример

Предположим, что у меня длинная хвостовая переменная (что я хочу, чтобы ось x была масштабирована по шкале):

library(PtProcess)
library(ggplot2)

set.seed(1)
lambda <- 1.5
a <- 1
pareto <- rpareto(1000,lambda=lambda,a=a)
x_pareto <- seq(from=min(pareto),to=max(pareto),length=1000)
y_pareto <- 1-ppareto(x_pareto,lambda,a)
df1 <- data.frame(x=x_pareto,cdf=y_pareto)

ggplot(df1,aes(x=x,y=cdf)) + geom_line() + scale_x_log10()

Long tail

И нормальная переменная:

set.seed(1)
mean <- 3
norm <- rnorm(1000,mean=mean)
x_norm <- seq(from=min(norm),to=max(norm),length=1000)
y_norm <- pnorm(x_norm,mean=mean)
df2 <- data.frame(x=x_norm,cdf=y_norm)

ggplot(df2,aes(x=x,y=cdf)) + geom_line()

Normal

Я хочу построить их бок о бок, используя ту же ось y.

Попытка # 1

Я могу сделать это с помощью граней, которые выглядят великолепно, но я не знаю, как сделать каждую ось X с другим масштабом (scale_x_log10() делает оба из них масштабируемыми в логах):

df1 <- cbind(df1,"pareto")
colnames(df1)[3] <- 'var'
df2 <- cbind(df2,"norm")
colnames(df2)[3] <- 'var'
df <- rbind(df1,df2)

ggplot(df,aes(x=x,y=cdf)) + geom_line() + 
       facet_wrap(~var,scales="free_x") + scale_x_log10()

Facets attempt

Попытка # 2

Используйте grid.arrange, но я не знаю, как сохранить обе области графика с одинаковым соотношением сторон:

library(gridExtra)
p1 <- ggplot(df1,aes(x=x,y=cdf)) + geom_line() + scale_x_log10() +
      theme(plot.margin = unit(c(0,0,0,0), "lines"),
            plot.background = element_blank()) +
      ggtitle("pareto")
p2 <- ggplot(df2,aes(x=x,y=cdf)) + geom_line() + 
      theme(axis.text.y = element_blank(), 
            axis.ticks.y = element_blank(), 
            axis.title.y = element_blank(),
            plot.margin = unit(c(0,0,0,0), "lines"),
            plot.background = element_blank()) +
      ggtitle("norm")
grid.arrange(p1,p2,ncol=2)

Grid attempt

PS: количество графиков может варьироваться, поэтому я не ищу ответ специально для 2 графиков

Ответы

Ответ 1

Расширение вашей попытки # 2, gtable может помочь вам. Если поля одинаковы в двух диаграммах, то единственные ширины, которые меняются на двух графиках (я думаю), являются пробелами, отмеченными метками метки метки оси y и текстом осей, что, в свою очередь, изменяет ширину панелей, Используя код из здесь, пробелы, сделанные текстом оси, должны быть одинаковыми, поэтому ширины двух областей панели должны быть одинаковыми, и поэтому пропорции должны быть одинаковым. Однако результат (без полей справа) выглядит не очень красивым. Поэтому я добавил немного поля справа от p2, а затем убрал ту же сумму слева от p2. Аналогично для p1: я добавил немного влево, но убрал ту же сумму вправо.

library(PtProcess)
library(ggplot2)
library(gtable)
library(grid)
library(gridExtra)

set.seed(1)
lambda <- 1.5
a <- 1
pareto <- rpareto(1000,lambda=lambda,a=a)
x_pareto <- seq(from=min(pareto),to=max(pareto),length=1000)
y_pareto <- 1-ppareto(x_pareto,lambda,a)
df1 <- data.frame(x=x_pareto,cdf=y_pareto)

set.seed(1)
mean <- 3
norm <- rnorm(1000,mean=mean)
x_norm <- seq(from=min(norm),to=max(norm),length=1000)
y_norm <- pnorm(x_norm,mean=mean)
df2 <- data.frame(x=x_norm,cdf=y_norm)

p1 <- ggplot(df1,aes(x=x,y=cdf)) + geom_line() + scale_x_log10() +
      theme(plot.margin = unit(c(0,-.5,0,.5), "lines"),
            plot.background = element_blank()) +
      ggtitle("pareto")
p2 <- ggplot(df2,aes(x=x,y=cdf)) + geom_line() + 
      theme(axis.text.y = element_blank(), 
            axis.ticks.y = element_blank(), 
            axis.title.y = element_blank(),
            plot.margin = unit(c(0,1,0,-1), "lines"),
            plot.background = element_blank()) +
      ggtitle("norm")

gt1 <- ggplotGrob(p1)
gt2 <- ggplotGrob(p2)

newWidth = unit.pmax(gt1$widths[2:3], gt2$widths[2:3])

gt1$widths[2:3] = as.list(newWidth)
gt2$widths[2:3] = as.list(newWidth)

grid.arrange(gt1, gt2, ncol=2)

enter image description here

ИЗМЕНИТЬ Чтобы добавить третий график вправо, нам нужно больше контролировать холст для печати. Одним из решений является создание нового файла gtable, который содержит пространство для трех графиков и дополнительное пространство для правильного поля. Здесь я позволяю полям на участках следить за интервалом между графиками.

p1 <- ggplot(df1,aes(x=x,y=cdf)) + geom_line() + scale_x_log10() +
      theme(plot.margin = unit(c(0,-2,0,0), "lines"),
            plot.background = element_blank()) +
      ggtitle("pareto")
p2 <- ggplot(df2,aes(x=x,y=cdf)) + geom_line() + 
      theme(axis.text.y = element_blank(), 
            axis.ticks.y = element_blank(), 
            axis.title.y = element_blank(),
            plot.margin = unit(c(0,-2,0,0), "lines"),
            plot.background = element_blank()) +
      ggtitle("norm")

gt1 <- ggplotGrob(p1)
gt2 <- ggplotGrob(p2)

newWidth = unit.pmax(gt1$widths[2:3], gt2$widths[2:3])

gt1$widths[2:3] = as.list(newWidth)
gt2$widths[2:3] = as.list(newWidth)

 # New gtable with space for the three plots plus a right-hand margin
gt = gtable(widths = unit(c(1, 1, 1, .3), "null"), height = unit(1, "null"))

# Instert gt1, gt2 and gt2 into the new gtable
gt <- gtable_add_grob(gt, gt1, 1, 1)
gt <- gtable_add_grob(gt, gt2, 1, 2)
gt <- gtable_add_grob(gt, gt2, 1, 3)

grid.newpage()
grid.draw(gt)

enter image description here