【问题标题】:Save multiple ggplots from a for loop in a single plot in a particular layout将来自 for 循环的多个 ggplots 保存在特定布局中的单个图中
【发布时间】:2020-09-07 13:01:53
【问题描述】:

我正在尝试绘制包含 35 个 ggplot 的单个图像。单个图像中的绘图顺序是固定的,如下所示。

我还想要网格图像中显示的空白网格。每个网格都应该有带有特定药物编号的图。我有一个数据框“drug_dctv2”,我正在拆分它,并制作一个列表以将数据读入 for 循环。

问题是:在plot_list[[i]]中,只有最后一个plot用i(1到35)保存了35次。我也不确定如何按网格中所示的特定顺序保存绘图。

通过我的互联网搜索,我找到了像“cowplot”和“gridextra”这样的库,但我找不到合适的方法来实现这些。 我制作了一个绘图布局文件,其中包含以下顺序的药物名称,如网格图像所示,我插入了“制表符”来代替空格。但我没有找到从那里开始的方法。 我是 R 新手。任何帮助和建议将不胜感激。

数据集如下图所示。每种药物都有 10 个数据点。

**Drug_name    conc    viab**
Drug_1       1         1.0265
Drug_1       0.1       1.2365
Drug_1       0.01      0.5896
--          --        --
Drug_2       1         2.0584
Drug_2       0.1       1.0277
Drug_2       0.01      1.5696
--          --        --
#
split <- split(file,rep(1:35,each=10)) #### this will be used in the for loop

    plot_list = list()
    for(i in 1:length(split))
    {
      data <- split[[i]]
      c <- data$conc
      v <- data$viab

    p = ggplot(data = data,aes(x=c,y=v))+geom_point()+ylim(0,1.5)+
        scale_x_continuous(trans='log10')+
          theme(axis.text = element_blank(),axis.title = element_blank()) +
          geom_line(data=line_data, aes(x=x,y=y2),color ="red",size=1)

      plot_list[[i]] = p
    }

提前谢谢你!!

【问题讨论】:

  • aes() 最适用于列名,而不是向量。试试改成aes(x = conc, y = viab)
  • 看看 facet_grid 能否解决您的问题,
  • 成功了。现在 plot_list[i] 有所有的情节。我还找到了另一种方法。我使用 aes_string() 而不是 aes() 并且它也适用于矢量。有什么建议让网格单一图像?谢谢@GregorThomas
  • 也许可以查看these answers。如果您遇到问题,请制作一个可重复的小示例(例如,可能 3 个图,每个图上有 1 或 2 个点,以及一个空白图),分享样本数据,展示您尝试过的方法,我们可以帮助您实现目标。
  • 至于顺序,你需要在某个地方定义它。作为开始,我建议将names() 添加到您的plot_list,并获取您希望它们绘制的顺序的向量。但是该向量应该是列优先还是行优先,以及空白应该如何是否包含在内,将取决于情节组合解决方案。

标签: r loops for-loop ggplot2 plot


【解决方案1】:

ggplot 与许多 tidyverse 软件包一样,使用延迟的非标准评估。在绘图构建之前(例如打印或保存),不会评估您在 aes 中提供的表达式。

您问题中的表达式是指在 for 循环中定义的向量 cv。这些向量在每次迭代时都会发生变化,但 aes 调用仅包含一个表达式,该表达式在 for 循环运行的环境中对 cv 的引用,因此图中使用了 cv 值是打印或保存绘图时可用的那些。

如 cmets 中所述,您可以直接使用数据框中的列,因为 ggplot 会在调用 ggplot() 时评估数据框。

如果您想继续使用cv,另一种方法是确保每次迭代都在独立的环境中运行,因此cv 的ggplot 引用指向不同的cv 每次循环迭代。例如,可以使用 lapply 调用替换 for 循环。

plot_list <- lapply(split, function(data_drug) {
      c <- data_drug$conc
      v <- data_drug$viab
      ggplot(data = data_drug,aes(x=c,y=v))+geom_point()+ylim(0,1.5)+
        scale_x_continuous(trans='log10')+
          theme(axis.text = element_blank(),axis.title = element_blank()) +
          geom_line(data=line_data, aes(x=x,y=y2),color ="red",size=1)
    })

这是一个很好的例子,其中 for 循环和 lapply 调用产生不同的结果,它是关于非标准评估和可变环境的一次很好的学习体验。

要合并这些图,请查看 cowplot::plot_grid https://wilkelab.org/cowplot/articles/plot_grid.html

这样的东西应该可以工作

library(cowplot)
plot_grid(
  plot_list[[35]], plot_list[[5]], plot_list[[3]], plot_list[[2]],
  plot_list[[34]], plot_list[[1]], plot_list[[4]], plot_list[[6]],
  plot_list[[32]], plot_list[[8]], NULL, NULL,
  plot_list[[30]], plot_list[[7]], plot_list[[33]] , NULL,
  labels = "AUTO", ncol = 4
)

您可以将所有函数参数放在一个列表中,然后使用 do.call 调用带有参数的函数:

plot_order <- c(
  35, 5, 3, 2,
  34, 1, 4, 6,
  32, 8, NA, NA
)

plot_grid_args <- c(plot_list[plot_order], list(ncol = 4))
do.call(plot_grid, plot_grid_args)

【讨论】:

  • 为了合并绘图,我已经尝试过这种使用 plot_grid() 的方法,但这并不是一种有效的方法。如果我有超过 100 个地块,那我该怎么办。 @zeehio
  • 作为cowplot 的替代方案,请参阅您问题中的cmets。它们使用 grid.arrange 链接到有关绘图顺序的相关 stackoverflow 问题。
【解决方案2】:

所以,我终于能够解决这个问题了。

我用药物的位置做了一个可变布局,因为它们在 split[i] 列表中。例如:drug_35 必须在网格中排在第一位,并且在 split[i] 列表中位于第 35 位,因此在“布局”中,变量 35 排在第一位,依此类推。

我制作了一个带有网格布局的文本文件,如上图所示,然后在 R 脚本中读取该文件,通过一些代码行,我能够使布局变量。为了简单起见,我没有在这里显示这些代码行。但是,我希望这个概念是明确的。

lay <- read.delim("layout.txt",stringsAsFactors = FALSE,sep = "\t", header = F)

lay1 = c(t(lay))

col_n = ncol(lay)
row_n = nrow(lay)

split <- split(file,rep(1:35,each=10))

## layout = 35 5 3 2 34 1 4 6 32 8 0 0 30 7 33 .....
## 0 means blank spaces

png("PLOT.png", width = 6, height = 10, units = "in", res = 400)
par(mfrow=c(row_n,col_n),mar=c(2,0.7,1.5,0.5))  ## margins: bottom, left, top and right

for(i in layout)
{
   if(i== 0) { frame(); next; }
   ## Here if 0 comes then the for loop will be skipped and frame() will generate a blank in the grid image

data <- split[[i]]
      c <- data$conc
      v <- data$viab

plot(c,v,xlab = NULL,ylab = NULL, axes = F,log = "x")
}
dev.off()

【讨论】:

    猜你喜欢
    • 2017-08-30
    • 1970-01-01
    • 2014-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多