tryCatch - 命名空间?

4

我对R语言还比较陌生,对于tryCatch的正确使用感到困惑。我的目标是对大型数据集进行预测。如果预测结果无法存入内存,则希望通过拆分数据来解决问题。

目前,我的代码大致如下:

tryCatch({
  large_vector = predict(model, large_data_frame)
}, error = function(e) { # I ran out of memory
  for (i in seq(from = 1, to = dim(large_data_frame)[1], by = 1000)) {
    small_vector = predict(model, large_data_frame[i:(i+step-1), ])
    save(small_vector, tmpfile)
  }
  rm(large_data_frame) # free memory
  large_vector = NULL
  for (i in seq(from = 1, to = dim(large_data_frame)[1], by = 1000)) {
    load(tmpfile)
    unlink(tmpfile)
    large_vector = c(large_vector, small_vector)
  }
})

重点是,如果没有发生错误,large_vector会按预期填充我的预测。如果发生错误,large_vector似乎只存在于错误代码的名称空间中 - 这很有道理,因为我将其声明为函数。出于同样的原因,我收到一个警告,称无法删除large_data_frame
不幸的是,这种行为并不是我想要的。我希望能够在我的错误函数内部分配变量large_vector。我想到了一种可能性是指定环境并使用assign。因此,在我的错误代码中,我将使用以下语句:
rm(large_data_frame, envir = parent.env(environment()))
[...]
assign('large_vector', large_vector, parent.env(environment()))

然而,这种解决方案对我来说似乎有点不够优美。 我想知道是否有任何可能通过“干净”的代码实现我的目标?

【编辑】因为我主要是为了说明问题而不是提供一个可工作的示例,所以有些人感到困惑。 这里是一个最小的示例,显示名称空间问题:

# Example 1 : large_vector fits into memory
rm(large_vector)
tryCatch({
  large_vector = rep(5, 1000)
}, error = function(e) {
  # do stuff to build the vector
  large_vector = rep(3, 1000)
})
print(large_vector)  # all 5

# Example 2 : pretend large_vector does not fit into memory; solution using parent environment
rm(large_vector)
tryCatch({ 
  stop();  # simulate error
}, error = function(e) {
  # do stuff to build the vector
  large_vector = rep(3, 1000)
  assign('large_vector', large_vector, parent.env(environment()))
})
print(large_vector)  # all 3

# Example 3 : pretend large_vector does not fit into memory; namespace issue
rm(large_vector)
tryCatch({ 
  stop();  # simulate error
}, error = function(e) {
  # do stuff to build the vector
  large_vector = rep(3, 1000)
})
print(large_vector)  # does not exist

请检查我的答案的第二次编辑,我在那里添加了一个小提示,在某些情况下可能会很有用。 - Jouni Helske
3个回答

5
我会这样做:

我会像这样做:

res <- tryCatch({
  large_vector = predict(model, large_data_frame)
}, error = function(e) { # I ran out of memory
  ll <- lapply(split(data,seq(1,nrow(large_data_frame),1000)),
         function(x)
             small_vector = predict(model, x))
  return(ll)
})
rm(large_data_frame)
if(is.list(ll)) 
  res <- do.call(rbind,res)

这个想法是,如果你的内存不足,返回一个预测结果列表。

注意,由于我们没有可重现的示例,因此我不确定结果。


那真的有效!我之前尝试从错误函数中返回变量,但是我错过了将tryCatch调用的返回值分配给一个变量(在这种情况下是res)。非常感谢! - Jenny
1
@Jenny 注意,你可以在 finally 块中使用 deallocate。finally = {rm(large_data_frame)} - agstudy
+1,这比我试图按照原始问题的精神制作的解决方案要好得多。而@agstudy的评论使我的答案变得毫无意义 :) - Jouni Helske
1
@Hemmo 我看到了 :) 但我喜欢你的简化版本来解释你的目的(逐行解释代码)。 - agstudy
解决方案中似乎有一个小错误...应该将 if(is.list(ll)) 更改为 if(is.list(res)) - Lazarus Thurston
天啊,这是一个九年前的答案。如果有 bug 的话,让我们为那些来到这里的人纠正它。 - Lazarus Thurston

4

编辑:让我们再试一次:

您可以使用 tryCatch 函数的 finally 参数:

step<-1000
n<-dim(large_data_frame)[1]
large_vector <- NULL
tryCatch({
  large_vector <- predict(model, large_data_frame) 
}, error = function(e) { # ran out of memory
  for (i in seq(from = 1, to = n, by = step)) {
    small_vector <- predict(model, large_data_frame[i:(i+step-1),]) #predict in pieces
    save(small_vector,file=paste0("tmpfile",i)) #same pieces
  }  
 rm(large_data_frame) #free memory

},finally={if(is.null(large_vector)){ #if we run out of memory
   large_vector<-numeric(n) #make vector
   for (i in seq(from = 1, to = n, by = step)){
     #collect pieces
     load(paste0("tmpfile",i)) 
     large_vector[i:(i+step-1)] <- small_vector
   }
}})

以下是简化版本,以便了解正在发生的事情:

large_vector<-NULL
rm(y)
tryCatch({
  large_vector <- y 
}, error = function(e) {# y is not found
  print("error")
},finally={if(is.null(large_vector)){
 large_vector<-1
}})
> large_vector
[1] 1

编辑2: 关于作用域的另一个提示可能对您有用(尽管在这种情况下,您不想事先声明large_vector):来自R-help的<<-运算符:

<< -和->>运算符通常仅在函数中使用,并且会通过父环境搜索变量的现有定义...

因此,您可以像这样使用上面的示例代码:

large_vector<-NULL
rm(y)
tryCatch({
  large_vector <- y 
}, error = function(e) {# y is not found
  large_vector <<- 1
  print("error")
})
> large_vector
[1] 1

代码主要是为了说明我的问题,因此我说它看起来“大致”是这样的。很抱歉这种方式无法再现。实际上,您的解决方案要求我在tryCatch子句之前分配large_vector,但由于内存限制,这是不可能的。此外,在错误函数内部分配large_vector仍然不会发生(因为它仍然是具有自己命名空间的函数)。 - Jenny
好的,我原本认为问题出在predict函数内存分配上,而不是large_vector的分配上。但你关于第二个问题是正确的,这里赋值当然行不通。抱歉。 - Jouni Helske
@Jenny,我编辑了我的答案以展示另一种解决方案。虽然@agstudy的答案更整洁,因为它不需要任何临时文件等,但这可能是tryCatchfinally参数的一个例子。 - Jouni Helske

0

下面的代码非常容易理解。问题在于,错误函数内部的任何内容默认情况下都不会应用于父环境。

b=0

如上所述,以下代码无法正常工作:

tryCatch(expr = {stop("error1")}, error=function(e) {b=1})
b

解决方案1:分配给父环境

tryCatch(expr = {stop("error2")}, error=function(e) {assign(x = "b", value = 2, envir = parent.env(env = environment()))})
b

解决方案2:最简单的方法(仅适用于在exprerror中都将值分配给b的情况)

b = tryCatch(expr = {stop("error3")}, error=function(e) {b=3;return(b)})
b


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接