【问题标题】:How can I reuse a R variable from one shell script to the other?如何将 R 变量从一个 shell 脚本重用到另一个?
【发布时间】:2021-01-24 23:19:27
【问题描述】:

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

有没有办法将数据集保存在内存中以连续运行多个分析(即$ ./my_script.R --parameter1 a2 --parameter2 b2 等)?也许使用全球环境?
谢谢!

【问题讨论】:

  • 您是说在 bash 提示符下,您调用 ./my_script.R...,当它完成时,您又回到 bash 提示符并希望运行相同(或不同)的 R 脚本并拥有该数据会一直驻留以供第二个 R 实例重用?
  • @r2evans 是的,就是这样。
  • 这就是问题所在:当myscript.R 完成时,R 退出。它为计算目的而请求和分配的内存(并保存.Rds 文件的内容)已被释放并返回给操作系统。如果您希望/需要 R 对象驻留在内存中,则必须以某种方式保持 R 处于活动状态,这可能与这种多个(不同)脚本文件的方法不兼容。
  • 将某种形式的 R 保留在常驻内存中的一种方法是提供某种形式的 R service,它可能是 shiny(在这种情况下不太可能), plumber,或Rserve。每种都有优点和缺点,它们都有包袱(例如,学习曲线、运行过程的开销、配置、身份验证、状态管理等)。
  • 可能改进整体流程的一种方法是:在一次调用中允许多次迭代,而不是提供--param1 a1 --param2 b1。例如,您可能支持--param1-like args(与当前功能没有变化),但还添加了一个选项--params-csv somefile.csv,其中 CSV 文件是一个 2 列 csv,其中包含一对或多对 a1,b1,一个每行执行。您的脚本需要进行调整以适应此迭代,但您可以在加载 一次 后保持您的大型 .Rds 常驻。

标签: r shell scripting global-variables


【解决方案1】:

解决该问题的一种方法是允许用户在脚本调用时指定多对参数,以便程序可以一次遍历所有参数(只需一个启动成本)。

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

  1. library(optparse),为了便于争论。还有其他的,什么都不需要,我发现它让事情看起来很容易。
  2. 脚本能够知道它是被获取(并且运行一些代码,对开发/测试很有用)还是从命令行运行(这会触发一些代码运行)。这类似于 python 的 if __name__ == '__main__': 技巧,我刚才回答为 https://stackoverflow.com/a/47932989/3358272

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

#!/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 块内做尽可能少的工作,以便我在上面编写的函数可以交互使用或与脚本一起使用。这只是一种组织方式代码。)

此脚本支持三种模式:

  • 你的默认调用

    $ 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"]}
    

    一次一个“工作”。

  • 逗号分隔的多个参数,如

    $ 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 的逗号分隔数组是可信的,应该稍微分解一下以测试不相等的配对,以及错误或做一些有意义的事情;截至目前,它将失败
  • 一般来说,这里几乎没有错误检查,但这是上下文相关的

【讨论】:

    猜你喜欢
    • 2019-09-18
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 2017-11-07
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    • 2012-01-11
    相关资源
    最近更新 更多