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编写可执行脚本不是难事,棘手的是如何使脚本从文件和/或管道中读取。

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

  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代码的次优方式。