3

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

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

关于如何做到这一点的任何想法?

4

3 回答 3

4

编写可执行脚本并不是困难的部分,棘手的是如何使脚本从文件和/或管道中读取。我在这里写了一个通用的函数: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 -
于 2013-07-30T17:22:52.553 回答
2

当您拥有整个 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(...)
于 2013-07-30T18:37:19.360 回答
0

您需要在每个脚本的顶部添加一行以从stdin. 通过这个答案

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

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

于 2013-07-30T17:22:35.427 回答