解决该问题的一种方法是允许用户在脚本调用时指定多对参数,以便程序可以一次遍历所有参数(只需一个启动成本)。
这是一个使用了一些东西的示例脚本:
-
library(optparse),为了便于争论。还有其他的,什么都不需要,我发现它让事情看起来很容易。
- 脚本能够知道它是被获取(并且不运行一些代码,对开发/测试很有用)还是从命令行运行(这会触发一些代码运行)。这类似于 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 的逻辑是 --param1 和 2 的逗号分隔数组是可信的,应该稍微分解一下以测试不相等的配对,以及错误或做一些有意义的事情;截至目前,它将失败
- 一般来说,这里几乎没有错误检查,但这是上下文相关的