R-通过管道连接脚本



我有许多R脚本,我想使用UNIX风格的管道将它们链接在一起。每个脚本将采用一个数据帧作为输入,并提供一个数据框架作为输出。例如,我正在想象这样的东西,它将在R的批处理模式下运行。

  cat raw-input.Rds | step1.R | step2.R | step3.R | step4.R > result.Rds

有没有想过如何做到这一点?

编写可执行脚本并不是困难的部分,棘手的是如何使脚本从文件和/或管道中读取。我在这里写了一个有点通用的函数:https://stackoverflow.com/a/15785789/1201032

以下是I/O采用csv文件形式的示例:

您的step?.R文件应该如下所示:

#!/usr/bin/Rscript
OpenRead <- function(arg) {
   if (arg %in% c("-", "/dev/stdin")) {
      file("stdin", open = "r")
   } else if (grepl("^/dev/fd/", arg)) {
      fifo(arg, open = "r")
   } else {
      file(arg, open = "r")
   }
}
args  <- commandArgs(TRUE)
file  <- args[1]
fh.in <- OpenRead(file)
df.in <- read.csv(fh.in)
close(fh.in)
# do something
df.out <- df.in
# print output
write.csv(df.out, file = stdout(), row.names = FALSE, quote = FALSE)

你的csv输入文件应该看起来像:

col1,col2
a,1
b,2

现在这应该工作了:

cat in.csv | ./step1.R - | ./step2.R -

-令人讨厌,但却是必要的。还要确保运行类似chmod +x ./step?.R的程序,使脚本可执行。最后,您可以将它们存储在添加到PATH的目录中(并且没有扩展),这样您就可以像这样运行它:

cat in.csv | step1 - | step2 -

当你有整个R环境可用时,你到底为什么要把你的工作流程塞进管道里,我无法理解。

制作包含以下内容的main.r

source("step1.r")
source("step2.r")
source("step3.r")
source("step4.r")

就是这样。您不必将每个步骤的输出转换为串行格式;相反,您可以保留所有的R对象(数据集、拟合模型、预测值、晶格/ggplot图形等),为下一步处理做好准备。如果内存有问题,可以在每个步骤结束时rm任何不需要的对象;或者,每个步骤都可以使用environment,完成后将其删除,首先将任何所需的对象导出到全局环境。


如果需要模块化代码,您可以按照如下方式重新构建您的工作流程。将每个文件完成的工作封装到一个或多个函数中。然后使用适当的参数调用main.r中的这些函数。

source("step1.r")  # defines step1_read_input, step1_f2
source("step2.r")  # defines step2_f2
source("step3.r")  # defines step3_f1, step3_f2, step3_f3
source("step4.r")  # defines step4_write_output
step1_read_input(...)
step1_f2(...)
....
step4write_output(...)

您需要在每个脚本的顶部添加一行,以便从stdin中读取。通过这个答案:

in_data <- readLines(file("stdin"),1)

您还需要将每个脚本的输出写入stdout()

最新更新