如何将一个 shell 脚本中的 R 变量重用到另一个脚本?

How can I reuse a R variable from one shell script to the other?

我有一个 R 管道可以对一个大数据集进行 运行 分析。目前,我可以通过从终端调用我的脚本并为其提供我的分析参数来开始分析。 $ ./my_script.R --parameter1 a1 --parameter2 b1
该脚本从 .Rds 文件加载数据集,但每次启动脚本时加载时间超过一分钟。

有没有办法将数据集保存在内存中,以便连续进行 运行 多次分析(意味着 $ ./my_script.R --parameter1 a2 --parameter2 b2 等)?可以使用全局环境吗?
谢谢!

解决该问题的一种方法是允许用户在脚本调用时指定多对参数,这样程序就可以一次遍历所有参数(只需要一个 startup-cost ).

这是一个使用了一些东西的示例脚本:

  1. library(optparse),为了便于争论。还有其他的,没有什么是必须的,我觉得这样看起来很简单。
  2. 脚本能够知道它是被源代码(和不是 运行一些代码,对dev/testing有用)还是运行 从命令行(这将触发一些代码到 运行)。这类似于 python 的 if __name__ == '__main__': 技巧,我刚才回答的是 .

它们都不是绝对必要的,但我发现它有助于演示如何构建脚本,以便您可以促进 “一个或多个” 类型的操作。

#!/usr/bin/env r
startup <- function() {
  message(Sys.time(), " Some expensive data load ...")
  Sys.sleep(3)
}

func1 <- function(x, y) {
  message(Sys.time(), " Called with (x,y): ", jsonlite::toJSON(list(x=x,y=y)))
}

if (sys.nframe() == 0L) {
  library(optparse)
  P <- OptionParser()
  P <- add_option(P, c("--param1"), dest = "p1", type = "character",
                  help = "Parameter 1", metavar = "P1")
  P <- add_option(P, c("--param2"), dest = "p2", type = "character",
                  help = "Parameter 2", metavar = "P2")
  P <- add_option(P, c("--param-csv"), dest = "pcsv", type = "character",
                  help = "CSV file with parameters in each column", metavar = "FILE")
  args <- parse_args(P, commandArgs(trailingOnly = TRUE))

  if (!is.null(args$pcsv)) {
    if (!file.exists(args$pcsv)) {
      stop("file not found: ", sQuote(args$pcsv))
    }
    params <- read.csv(args$pcsv, header = FALSE)
    if (!ncol(params) >= 2L) {
      stop("file does not have (at least) 2 columns")
    }
  } else {
    params <- data.frame(
      p1 = sapply(strsplit(args$p1, "[,[:space:]]+")[[1]], trimws),
      p2 = sapply(strsplit(args$p2, "[,[:space:]]+")[[1]], trimws)
    )
  }

  startup()

  for (rownum in seq_len(nrow(params))) {
    func1(params[[1]][rownum], params[[2]][rownum])
  }  
}

为了这个演示,startup 是你加载你的 .Rds 文件(这里需要 3 秒),func1 是你可能进行的任何处理的其余部分正在做。 (作为一般提示,我倾向于在 sys.nframe() == 0 块中做尽可能少的 work,以便我在上面编写的函数可以交互使用或与脚本一起使用。它是只是组织代码的一种方式。)

此脚本支持三种模式:

  • 你的默认调用

    $ Rscript 64287443.R --param1 foo1 --param2 bar1
    2020-10-09 15:33:48 Some expensive data load ...
    2020-10-09 15:33:51 Called with (x,y): {"x":["foo1"],"y":["bar1"]}
    

    一次一个“工作”。

  • comma-separated 多个参数,如

    $ Rscript 64287443.R --param1 foo1,foo2 --param2 bar1,bar2
    2020-10-09 15:33:55 Some expensive data load ...
    2020-10-09 15:33:58 Called with (x,y): {"x":["foo1"],"y":["bar1"]}
    2020-10-09 15:33:58 Called with (x,y): {"x":["foo2"],"y":["bar2"]}
    

    相当于运行宁

    $ Rscript 64287443.R --param1 foo1 --param2 bar1
    $ Rscript 64287443.R --param1 foo2 --param2 bar2
    

    除了它只产生启动成本一次

  • 作业的 CSV 文件,每列一个参数。

    $ cat params.csv
    foo1,bar1
    foo2,bar2
    foo3,bar3
    
    $ Rscript 64287443.R --param-csv params.csv
    2020-10-09 15:35:15 Some expensive data load ...
    2020-10-09 15:35:18 Called with (x,y): {"x":["foo1"],"y":["bar1"]}
    2020-10-09 15:35:18 Called with (x,y): {"x":["foo2"],"y":["bar2"]}
    2020-10-09 15:35:18 Called with (x,y): {"x":["foo3"],"y":["bar3"]}
    

待办事项:

  • strsplit --param12 的 comma-separated 数组的逻辑是信任的,应该稍微分解一下以测试不相等的配对,或者错误或做一些有意义的事情;截至目前,它将失败
  • 总的来说,这里几乎没有错误检查,但那是context-sensitive