Подтвердить что ты не робот

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

Я хотел бы разместить четыре сюжета на одной странице. Этикетки осей должны быть напечатаны только на самом ободе, то есть на осях оси X только для нижних диаграмм, и на оси y для левых диаграмм. Это происходит как для названия оси в целом, так и для отдельных меток. Я могу сгенерировать что-то в этих строках, используя следующий код:

pdf(file = "ExampleOutput.pdf",
    width = 6.61,
    height = 6.61,
    pointsize = 10
    )
set.seed(42)
catA <- factor(c("m100", "m500", "m1000", "m2000", "m3000", "m5000"))
catB <- factor(20:28)
samples <- 100
rsample <- function(v) v[ceiling(runif(samples, max=length(v)))]
Tab <- data.frame(catA = rsample(catA),
                  catB = rsample(catB),
                  valA = rnorm(samples, 150, 8),
                  valB = pmin(1,pmax(0,rnorm(samples, 0.5, 0.3))))
par(mfrow = c(2,2))
for (i in 0:3) {
  x <- Tab[[1 + i %% 2]]
  plot(x, Tab[[3 + i %/% 2]],
       xlab = if (i %/% 2 == 1) "Some Categories" else NULL,
       ylab = if (i %% 2 == 0) "Some Values" else NULL,
       axes = FALSE
       )
  axis(side = 1,
       at=1:nlevels(x),
       labels = if (i %/% 2 == 1) levels(x) else FALSE)
  axis(side = 2, labels = (i %% 2 == 0))
  box(which = "plot", bty = "l")
}
par(mfrow = c(1,1))
dev.off()

Я буду приветствовать предложения о том, как улучшить мои команды построения графиков, возможно, избегать опускания осей и L в левом нижнем углу вручную. Но это только кроме того.

Результат этой последовательности выглядит следующим образом:

Current output

Проблема здесь в огромном количестве потраченных впустую пробелов. У меня сложилось впечатление, что R резервирует пространство для меток оси и тика, даже если они не используются. Вследствие этого потраченного пространства, для левой нижней диаграммы, только каждый второй x-тик фактически получает помеченную, что здесь действительно плохо.

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

Desired output

Как я могу достичь такого макета?

4b9b3361

Ответ 1

Вот небольшая модификация общего сюжета, который вы показываете, предполагая, что метки оси y и x относятся ко всем графикам. Он использует внешний край, чтобы содержать метку оси, которую мы добавляем с помощью title(), используя аргумент outer = TRUE. Эффект несколько похож на маркировку в строках ggplot2 или решетки.

Ключевая строка здесь:

op <- par(mfrow = c(2,2),
          oma = c(5,4,0,0) + 0.1,
          mar = c(0,0,1,1) + 0.1)

который задает параметры графика (значения, находящиеся до вызова, сохраняются в op). Мы используем строки 5 и 4 на сторонах 1 и 2 для внешнего поля, что является обычным числом для параметра mar. Границы области участка (mar) по 1 строке каждый добавляются в верхнюю и правую стороны, чтобы дать небольшую комнату между графиками.

Знаки оси добавляются после цикла for() с помощью

title(xlab = "Some Categories",
      ylab = "Some Values",
      outer = TRUE, line = 3)

Весь script:

set.seed(42)
catA <- factor(c("m100", "m500", "m1000", "m2000", "m3000", "m5000"))
catB <- factor(20:28)
samples <- 100
rsample <- function(v) v[ceiling(runif(samples, max=length(v)))]
Tab <- data.frame(catA = rsample(catA),
                  catB = rsample(catB),
                  valA = rnorm(samples, 150, 8),
                  valB = pmin(1,pmax(0,rnorm(samples, 0.5, 0.3))))
op <- par(mfrow = c(2,2),
          oma = c(5,4,0,0) + 0.1,
          mar = c(0,0,1,1) + 0.1)
for (i in 0:3) {
  x <- Tab[[1 + i %% 2]]
  plot(x, Tab[[3 + i %/% 2]], axes = FALSE)
  axis(side = 1,
       at=1:nlevels(x),
       labels = if (i %/% 2 == 1) levels(x) else FALSE)
  axis(side = 2, labels = (i %% 2 == 0))
  box(which = "plot", bty = "l")
}
title(xlab = "Some Categories",
      ylab = "Some Values",
      outer = TRUE, line = 3)
par(op)

который производит

enter image description here

Ответ 2

Просто манипулируйте своими параметрами в par. Аргумент mar управляет размером поля для отдельного участка. Измените par на это:

par(mfrow = c(2,2), mar=c(1, 4, 1, 1) + 0.1)#it goes c(bottom, left, top, right) 

Ответ 3

В ответ на большой ответ на от Gavin Simpson, теперь я использую следующее решение:

par(mfrow = c(2, 2),     # 2x2 layout
    oma = c(2, 2, 0, 0), # two rows of text at the outer left and bottom margin
    mar = c(1, 1, 0, 0), # space for one row of text at ticks and to separate plots
    mgp = c(2, 1, 0),    # axis label at 2 rows distance, tick labels at 1 row
    xpd = NA)            # allow content to protrude into outer margin (and beyond)

Результат выглядит следующим образом:

Resulting image

Как вы можете видеть, этого достаточно, чтобы разрешить печать всех ярлыков меток. Если бы это было не так, то согласно комментарий Gavin добавление cex.axis со значением меньше 1 в список параметров должно помочь уменьшить размер шрифта там.

Ответ 4

Вам нужна условная оценка, которая присваивает значениям par('mar'), которые соответствуют позиционированию; Вот пример кода (внутри вашего цикла), который проверяет "положение x-layout-position":

    pdf(file = "ExampleOutput2.pdf",
    width = 6.61,
    height = 6.61,
    pointsize = 10
    )
set.seed(42)
catA <- factor(c("m100", "m500", "m1000", "m2000", "m3000", "m5000"))
catB <- factor(20:28)
samples <- 100
rsample <- function(v) v[ceiling(runif(samples, max=length(v)))]
Tab <- data.frame(catA = rsample(catA),
                  catB = rsample(catB),
                  valA = rnorm(samples, 150, 8),
                  valB = pmin(1,pmax(0,rnorm(samples, 0.5, 0.3))))
par(mfrow = c(2,2), mar= c(3, 4, 1, 1) + 0.1)
for (i in 0:3) {
  x <- Tab[[1 + i %% 2]]
  plot(x, Tab[[3 + i %/% 2]], mar= if(i %/%2 == 0) {c(4, 4, 1, 1) + 0.1 
                                              }else{c(1, 1, 1, 1) + 0.1},
       xlab = if (i %/% 2 == 1) "Some Categories" else NULL,
       ylab = if (i %% 2 == 0) "Some Values" else NULL,
       axes = FALSE
       )
  axis(side = 1,
       at=1:nlevels(x),
       labels = if (i %/% 2 == 1) levels(x) else FALSE)
  axis(side = 2, labels = (i %% 2 == 0))
  box(which = "plot", bty = "l")
}
par(mfrow = c(1,1))
dev.off()

Вам нужно будет отрегулировать это так, как вам нужно, поскольку оно обрабатывает только два крайних условия, и у вас действительно есть 4 отдельных условия (2 ниже обоих требуется больше нижнего пространства, а справа - меньшее левое пространство и два выше (также с различными требованиями). Если вы уменьшите значение "mar" во всем мире, оно отключит ваши метки x и y, как можно увидеть в потере значений xlab, когда вы просто отбрасываете этот код в свой цикл.