R-通过管道连接脚本
我有很多R脚本,我想使用UNIX风格的管道将它们链接在一起。每个脚本将获取一个数据帧作为输入,并提供一个数据帧作为输出。例如,我想象这样的东西会在R的批处理模式下运行R-通过管道连接脚本,r,unix,pipe,R,Unix,Pipe,我有很多R脚本,我想使用UNIX风格的管道将它们链接在一起。每个脚本将获取一个数据帧作为输入,并提供一个数据帧作为输出。例如,我想象这样的东西会在R的批处理模式下运行 cat raw-input.Rds | step1.R | step2.R | step3.R | step4.R > result.Rds 关于如何实现这一点,您有什么想法吗?您需要在每个脚本的顶部添加一行,以便从stdin读取。通过: in_data编写可执行脚本不是难事,棘手的是如何使脚本从文件和/或管道中读取。
cat raw-input.Rds | step1.R | step2.R | step3.R | step4.R > result.Rds
关于如何实现这一点,您有什么想法吗?您需要在每个脚本的顶部添加一行,以便从
stdin
读取。通过:
in_data编写可执行脚本不是难事,棘手的是如何使脚本从文件和/或管道中读取。我在这里写了一个比较一般的函数:
以下是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)
cat in.csv | step1 - | step2 -
现在,这应该是可行的:
cat in.csv | ./step1.R - | ./step2.R -
-
虽然烦人,但却是必要的。还要确保运行类似于chmod+x./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)
cat in.csv | step1 - | step2 -
我无法理解为什么在整个R环境都可用的情况下,还要将工作流塞进管道中
制作一个包含以下内容的main.r
:
source("step1.r")
source("step2.r")
source("step3.r")
source("step4.r")
就这样。您不必将每个步骤的输出转换为串行格式;相反,您可以将所有R对象(数据集、拟合模型、预测值、晶格/ggplot图形等)保持原样,以便下一步处理。如果内存有问题,您可以在每个步骤结束时rm
任何不需要的对象;或者,每个步骤都可以使用环境
,完成后将删除该环境,首先将所有必需的对象导出到全局环境
如果需要模块化代码,您可以按如下方式重新构建您的工作流。将每个文件完成的工作封装到一个或多个函数中。然后在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(...)
你想知道如何做这件事的R代码吗?也许是通过一系列的source
调用?只需通过命令行将它们连接在一起……可能与编写函数的原因相同。脚本就像您编写的构建块,因此您可以以后以任何方式组合它们。此外,脚本不仅限于R:它们可以用您喜欢的任何语言编写。而且你可以在指尖得到所有的unix好东西(例如,grep
)。@flodel但这些不是我们谈论的通用脚本。这些是R脚本,它们在一个允许比字节流更丰富的对象的环境中执行。此外,R有许多(太多?)模拟操作系统级实用程序的函数;如果您需要更多,那么很可能无论如何都不应该使用R。此外,如果您想要R中的模块化代码,比如函数,那么您可以使用。。。功能。如果source
ing一组函数定义对您来说不够正式,您可以将它们放入一个包中。如果您所知道和使用的只是交互式会话中的R。OP已经在编写脚本的事实告诉我他已经超出了这个简单的用法。@flodel???交互使用与此无关。关键是管道是编写模块化R代码的次优方式。