如何处理R语言中大量的图表

5

我有一个for循环,它会产生60个图形。我想将所有这些图形保存在一个文件中。

如果我设置par(mfrow=c(10,6)),则会出现以下错误:Error in plot.new() : figure margins too large

我该怎么做呢?

我的代码如下:

pdf(file="figure.pdf")
par(mfrow=c(10,6))
for(i in 1:60){
  x=rnorm(100)
  y=rnorm(100)
  plot(x,y)
}
dev.off()

这应该是什么样子?如果在一个图形设备/图形输出中绘制60个图,那么这将不再可读吗? - Jen Bohold
我需要一个图表。我不知道它是否可读。至少我可以绘制30个图表的2个文档。但我需要类似于那样的东西。 - Donbeo
抱歉 @gung,我认为你可以迁移这篇帖子。 - Donbeo
2
你原有的代码似乎是将60个子图挤入一个单一的图中(!)。这样的设计最多也只能算是可怜。@gung提供的答案将会给你60个独立的图。 - gregmacfarlane
5个回答

5
您在循环中使用的默认图表未有效利用空间。如果您查看单个图表,您会发现它具有较大的边距,包括轴和边缘之间以及绘图区域和轴文本之间。实际上,这占用了很多空间。
其次,默认的pdf函数创建小页面,大小为7乘7英寸。这不是一个大的绘图纸张。
因此,尝试在7乘7英寸的纸张上绘制10 x 6或12 x 5的图表,就像试图在非常小的空间中挤入很多空白一样。
要成功,您必须查看par的margin-options(mar,mai,oma和omi),可能还有更多。使用以下命令查询文档。
?par

除此之外,您可能考虑不显示所有60个子图的轴文本、刻度线、刻度标签和标题,这样也能节省空间。
但是有人已经为您做了一些工作。看看lattice包或ggplot2,它们有一些制作表格式子图的出色方法。
但还有一个紧迫的问题:您想用60个子图显示什么?

更新

看到您要做的事情,这里有一个在ggplot2中使用分面绘图的小例子。它使用jrnold的ggthemes中的Tufte主题,稍微修改了一下函数后的行。

library(ggplot2)
library(scales)

#### Setup the `theme` for the plot, i.e. the appearance of background, lines, margins, etc. of the plot.
##   This function returns a theme-object, which ggplot2 uses to control the appearance.
theme_tufte <- function(ticks=TRUE, base_family="serif", base_size=11) {
  ret <- theme_bw(base_family=base_family, base_size=base_size) +
    theme(
      legend.background = element_blank(),
      legend.key        = element_blank(),
      panel.background  = element_blank(),
      panel.border      = element_blank(),
      strip.background  = element_blank(),
      plot.background   = element_blank(),
      axis.line         = element_blank(),
      panel.grid = element_blank())
  if (!ticks) {
    ret <- ret + theme(axis.ticks = element_blank())
  }
  ret
}

## Here I modify the theme returned from the function,
theme <- theme_tufte() + theme(panel.margin=unit(c(0,0,0,0), 'lines'),     panel.border=element_rect(colour='grey', fill=NA))
## and instruct ggplot2 to use this theme as default.
theme_set(theme)

#### Some data generation.
size = 60*30
data <- data.frame(x=runif(size), y=rexp(size)+rnorm(size), mdl=sample(60,size, replace=TRUE))

#### Main plotting routine.
ggplot(data, aes(x,y, group=mdl)) ## base state of the plot to be used on all "layers", i.e. which data to use and which mappings to use (x should use x-variable, y should use the y-variable
  + geom_point()                  ## a layer that renders data as points, creates the scatterplot
  + stat_quantile(formula=y~x)    ## another layer that adds some statistics, in this case the 25%, 50% and 75% quantile lines.
  + facet_wrap(~ mdl, ncol=6)     ## Without this, all the groups would be displayed in one large plot; this breaks it up according to the `mdl`-variable.

使用 <code>ggplot2</code> 进行分面。

在使用 ggplot2 时的常见难题是将所有数据重组成数据框架。对于这个任务,reshape2plyr 包可能会很有用。 针对您的情况,我想象中您创建子图的函数既计算了估计值,又创建了绘图。这意味着您需要将函数拆分为计算估计值并将其返回到 data.frame,然后将其整合并传递给 ggplot


1
感谢您的回答。我有一个由60个模型组成的家族,我必须在每个模型上比较一种估计方法。唯一比较结果的方法是图形化的方式。最终,我必须在论文中报告这些图形。 - Donbeo
这似乎是最简单的方法。你能否更好地解释一下?假设只有两个图:a=rnorm(10) b=a*a c=rnorm(20) d=c 如果我想要绘制(a,b)和(d,c)怎么办? - Donbeo
ggplot2中的分面图旨在当所有子图的相应轴相同时使用。您所要求的不适用于分面,因为您想并排比较2 x 2个变量。在这种情况下,您应该使用par(mfcol)选项或layout函数。 - MrGumble
好的,谢谢我懂了。最后一个问题:如何在同一张图中同时绘制线图和直方图? - Donbeo
1
请在一个新问题中提出这个问题;然后您可以提供更多的细节,例如它是R的本地plot还是ggplot,数据的外观,代码等。 - MrGumble

4

将绘图输出为pdf文件:

X = matrix(rnorm(60*100), ncol=60)
Y = matrix(rnorm(60*100), ncol=60)

pdf(file="fileName.pdf")
  for(j in 1:60){
    plot(X[,j], Y[,j])
  }
dev.off()

2
在这个循环中,您还可以使用par(4,4)或类似的东西,在一页上输出多个图。 - Zach
抱歉,但为什么它应该起作用?我已经在使用PDF格式了。你是在说 par(mfrow=c(2,2)) 吗? - Donbeo

3
为了在页面或文档上放置许多图表(我创建过包含成千上万个图表的图像),将工作分开使用比较方便——R 负责单独创建图表,而其他软件则更适合于排列各种元素。如果这让你想起了电子表格或文字处理表格,那么我们的想法是一致的。
这张页面截图来自 PDF 文件,包含 200 多个统计图形。虽然它已经被大幅缩小(到名义大小的 40%)以遮盖专有数据,但原始文件具有与原始 R 图形相同的所有细节,可以轻松缩放至 1600%。
两种机制都运作得相当不错。对于数百个图表,一个导入和重新排序一组位图图像文件(.emf 或 .wmf)到 Word 文档中的小宏就足够了。为了获得更好的控制,我会转向类似的 Excel 宏。它由一个只包含列标题和行标题的空工作表驱动。(您可以在左侧和顶部看到它们。)该宏删除该工作表上除格式之外的所有内容,然后将每个可能的行和列标题组合成一个文件名,如果找到该文件,则将其导入到相应的单元格中。对于几千个图像,整个操作只需要几秒钟。
显然,R 和其他软件之间的这种通信机制很原始,只由一组具有标准命名约定的图像文件组成。但实现所有这些所需的代码很简短(尽管需要根据每种情况进行自定义),而且它可靠地工作。例如,如果将绘图代码封装在一个函数中,那么它将在循环内调用以创建许多类似的图表。在该函数的末尾添加几行代码以将图表保存到文件中,类似于以下内容:
path <- "W: <whatever>/"                # Folder for the output files
ext <- "wmf" # or "emf" or "png" or ... # Format (and extension) of the output
...
if (save) {
  outfile <- paste(path, paste(munge(well), munge(parm), sep="_"), sep="/")
  outfile <- paste(outfile, ext, sep=".")
  savePlot(filename=outfile, type=ext)
}

在这种情况下,每个绘图都由两个循环变量 wellparm 识别,它们都是字符串(对应于列和行标题)。创建可接受文件名的函数仅剥离标点符号,并用一个无害的占位符替换。
munge <- function(s) gsub("[[:punct:]]", "_", s)

一旦这些图像被导入到Word、Excel或其他你喜欢的地方,就很容易重新组织它们,放置其他材料周围等等,然后以PDF格式打印结果。


创建这些非常大的“小多个”(按照Tufte的术语)有一定的技巧。尽可能遵循Tufte的增加数据:墨水比率的原则,通过抹去不必要的材料来帮助清晰地呈现图形模式,即使在将表格大大缩小以便一次性理解所有行和列时,也能让图形模式清晰可见。虽然前面的图形是一个不好的例子,但单独的图形必须有坐标轴、网格线、标签等等,以便在缩放时可以详细阅读,但这种方法揭示模式的力量即使在这个规模下也是显而易见的。使图形可比较是至关重要的。在这个例子中,由于是时间序列,每个图都具有相同的x轴范围;在每一行(对应不同类型的观察)内,y轴的范围都是相同的;并且所有的颜色方案和符号化方法都是相同的。


1
很高兴你提到了Tufte。 另外,同样的想法也适用于LaTeX:将每个子图输出为pdf或ps文件,并编写一个脚本来编写插入所有图形到表格或子图中的LaTeX代码。 - MrGumble
@MrGumble 很高兴知道这可以在LaTeX中完成。 Excel解决方案特别好的地方是它的即时交互性:您可以重新排序行和列,剪切和粘贴它们之间,调整它们的大小(这会导致单元格中的图像相应地调整大小),格式化它们(这就是我图中粉色突出显示的原因)等等。我不认为LaTeX有任何这样的所见即所得能力。 - whuber

0
对于这种问题,有一个非常简单的解决方案,我发现设置一个大的“Windows”设备可以使窗口足够大以适应许多用途。
windows(50,50)
par(mfrow=c(10,6))
for(i in 1:60){
  x=rnorm(100)
  y=rnorm(100)
  plot(x,y)
}

或者在我的情况下,

windows(20,20)
plot(Plotting_I_Need_In_Rows_of_4, mfrow=c(4,4))

0

你也可以使用knitr。虽然它不能立即转换为基本图形(而且我现在必须运行),但使用ggplot很容易。

\documentclass{article}

\begin{document}

<<echo = FALSE, fig.keep='high', fig.height=3, fig.width=4>>=
require(ggplot2)
for (i in 1:10) print(ggplot(mtcars, aes(x = disp, y = mpg)) + geom_point())
@


\end{document}

以上代码将生成一个漂亮的多页PDF,其中包含所有图表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接