将多个对齐的图放在一页上时,避免浪费空间

时间:2012-11-05 20:37:55

标签: r plot

我想将四个地块放在一个页面上。轴标签应仅在最边缘打印,即仅用于底部图的 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

我如何实现这样的布局?

4 个答案:

答案 0 :(得分:57)

这是对您显示的一般图的略微修改,假设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中)。我们在第1和第2边使用54行作为外边距,这是mar参数的常用数字。每条1行的绘图区域边距(mar)都会添加到顶部和右侧,以便在绘图之间留出一点空间。

使用

for()循环后添加轴标签
title(xlab = "Some Categories",
      ylab = "Some Values",
      outer = TRUE, line = 3)

整个脚本是:

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

答案 1 :(得分:19)

严重依赖the answer from 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's comment,在参数列表中添加值小于1的cex.axis应该有助于减少那里的字体大小。

答案 2 :(得分:17)

只需在par中操纵您的参数即可。参数mar控制单个图的边距大小。将您的par更改为此:

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

答案 3 :(得分:2)

您需要一个条件评估,分配适合定位的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个独立的条件(下面两个需要更多的底部空间,右边需要更少的左空间和两个上面的空间(也有不同的要求)。如果你全局缩小'mar'值,它会切断你的x和y标签,这可以从xlab值的丢失中看出来,当你只将这个代码放入你的循环时。