R - 将函数存储在data.frame中

时间:2014-01-04 15:50:32

标签: r function lazy-evaluation

我想返回一个矩阵/ data.frame,每行包含参数和文件内容。

但是,可能有很多文件,所以我更喜欢我可以懒得加载文件,因此只有在请求实际内容时才会读取文件。如果as.func=F

,下面的函数会主动加载文件

如果它可以懒散地加载它们将是完美的,但是如果不是内容而是返回将读取内容的函数,那也是可以接受的。

我可以创建读取内容的函数(请参阅下面的as.func=T),但出于某种原因,我无法将其放入data.frame中返回。

load_parallel_results <- function(resdir,as.func=F) {
  ## Find files called .../stdout                                                                                                                                                                              
  stdoutnames <- list.files(path=resdir, pattern="stdout", recursive=T);
  ## Find files called .../stderr                                                                                                                                                                              
  stderrnames <- list.files(path=resdir, pattern="stderr", recursive=T);
  if(as.func) {
    ## Create functions to read them                                                                                                                                                                           
    stdoutcontents <-
      lapply(stdoutnames, function(x) { force(x); return(function() { return(paste(readLines(paste(resdir,x,sep="/")),collapse="\n")) } ) } );
    stderrcontents <-
      lapply(stderrnames, function(x) { force(x); return(function() { return(paste(readLines(paste(resdir,x,sep="/")),collapse="\n")) } ) } );
  } else {
    ## Read them                                                                                                                                                                                               
    stdoutcontents <-
      lapply(stdoutnames, function(x) { return(paste(readLines(paste(resdir,x,sep="/")),collapse="\n")) } );
    stderrcontents <-
      lapply(stderrnames, function(x) { return(paste(readLines(paste(resdir,x,sep="/")),collapse="\n")) } );
  }
  if(length(stdoutnames) == 0) {
    ## Return empty data frame if no files found                                                                                                                                                               
    return(data.frame());
  }

  ## Make the columns containing the variable values                                                                                                                                                           
  m <- matrix(unlist(strsplit(stdoutnames, "/")),nrow = length(stdoutnames),byrow=T);
  mm <- as.data.frame(m[,c(F,T)]);
  ## Append the stdout and stderr column                                                                                                                                                                       
  mmm <- cbind(mm,unlist(stdoutcontents),unlist(stderrcontents));
  colnames(mmm) <- c(strsplit(stdoutnames[1],"/")[[1]][c(T,F)],"stderr");
  ## Example:                                                                                                                                                                                                  
  ## parallel --results my/res/dir --header : 'echo {};seq {myvar1}' ::: myvar1 1 2 ::: myvar2 A B                                                                                                             

  ##  > load_parallel_results("my/res/dir")                                                                                                                                                                    
  ##       myvar1 myvar2 stdout      stderr                                                                                                                                                                    
  ##  [1,] "1"    "A"    "1 A\n1"    ""                                                                                                                                                                        
  ##  [2,] "1"    "B"    "1 B\n1"    ""                                                                                                                                                                        
  ##  [3,] "2"    "A"    "2 A\n1\n2" ""                                                                                                                                                                        
  ##  [4,] "2"    "B"    "2 B\n1\n2" ""                                                                                                                                                                        
  return(mmm);
}

背景

GNU Parallel有一个--results选项,以结构化方式存储输出。如果有1000000个输出文件,则可能很难管理它们。 R对此有好处,但如果你必须阅读所有1000000个文件只是为了获得参数1 =“Foo”和参数2 =“Bar”的那些文件,那将会非常慢。

3 个答案:

答案 0 :(得分:3)

不幸的是,我不认为你可以在data.frame列中保存一个函数。 但是您可以存储函数的已解析文本并在需要时对其进行评估:

e.g。

myFunc <- function(x) { print(x) }
# convert the function to text
funcAsText <- deparse(myFunc)

# convert the text back to a function
newMyFunc <- eval(parse(text=funcAsText))

# now you can use the function newMyFunc exactly like myFunc
newMyFunc("foo")

> [1] "foo"

编辑:
由于文件很多,我建议您只需存储一个指示文件类型的字符串,并创建一个理解类型并相应读取文件的函数;所以你可以在需要时通过传递类型和文件路径来调用它。

答案 1 :(得分:2)

(不阅读问题正文:)

您可以像这样在data.frame中存储函数:

df <- data.frame(fun = 1:3)
df$fun <- c(mean, sd, function(x) x^2)

我不确定这是否会破坏其他东西,因此请考虑从确实支持任意对象类型的相同命名包中使用tibbledata.table

答案 2 :(得分:1)

您可以使用2D列表存储您的功能。显然,你失去了一些用DF获得的检查,但这就是重点:

> funs <- c(replicate(5, function(x) NULL), replicate(5, function(y) TRUE))
> names <- as.list(letters[1:10])
> # df doesn't work
> df <- data.frame(names=names)
> df.2 <- cbind(df, funs)
Error in as.data.frame.default(x[[i]], optional = TRUE) : 
  cannot coerce class ""function"" to a data.frame
# but 2d lists do
> lst.2d <- cbind(funs, names)
> lst.2d[2, 1]
$funs
function (x) 
  NULL
> lst.2d[6, 1]
$funs
function (y) 
  TRUE