我有许多 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(...)