我使用循环导入并绑定多个名为“File_ij.xlsx”的 Excel 文件,其中 i 可以从 1 到 4,j 从 1 到 3。但是 i=3 或 j = 2 不存在,所以我一共有6个文件。这就是为什么我使用 tryCatch 来跳过丢失的文件并显示错误消息。下面的代码做了我想要的。但除了导入之外,我还想创建一个数据框来记录 i 和 j 的错误组合。这并不像我预期的那样工作。谁能告诉我为什么?代码如下:
library(readxl)
library(tidyverse)
df <- data.frame() # empty DF for storing imported data
missing_df <- data.frame(i = numeric(), j = numeric()) # empty DF for storing information about missing records
for (i in 1:4){
for (j in 1:3) {
tryCatch({
out <- read_xlsx(paste0("C:\\test\\File_", i, j, ".xlsx"))
df <- bind_rows(df, out)
},
error = function(e) {
message(paste0("The following do not exist: i = ", i, ", j = ", j)) # this works fine, message is displayed
missing_df <- bind_rows(missing_df, data.frame(i = i, j = j)) # This doesn't work as expected, rows are not appended to the DF
}
)
}
}
这与范围界定有关。如果抛出错误,则会调用错误处理程序function,它会跟踪自己的变量,甚至(如果它们与全局环境中的变量具有相同的名称)。这个例子说明了这个想法:
a <- 1
f <- function() {
cat("Before a is defined in the function it takes the value from global:", a, "\n")
a <- 2
cat("After a is defined in the function it takes the value from local:", a, "\n")
}
f()
# Before a is defined in the function it takes the value from global: 1
# After a is defined in the function it takes the value from local: 2
cat("After the function is called we only see the global scope:", a, "\n")
# After the function is called we only see the global scope: 1
您可以使用
<<-
在函数内的全局环境中赋值,但通常最好避免这种模式:
a <- 1
g <- function() {
a <<- 2
}
print(a)
# [1] 1
g() ## changes a on global scope
print(a)
# [1] 2
tryCatch
在
tryCatch
内,同样的想法也适用。主要代码在 global 范围内进行评估(也就是说,我们可以在 global 范围内修改变量,而不需要 <<-
),但是在错误处理程序(实际上是一个 函数)中,我们需要使用 <<-
在全局 sope 上分配:
even <- list()
even2 <- list()
odd <- list()
odd_bad <- list()
for (i in 1:2) {
for (j in 1:3) {
tryCatch({
stopifnot("i * j must be even" = i * j %% 2 == 0)
even2 <- c(even2, list(c(i, j))) ## works because this code is evaluated on global scope
even <<- c(even, list(c(i, j)))
}, error = function(err) {
odd_bad <- c(odd_bad, list(c(i, j)))
odd <<- c(odd, list(c(i, j)))
})
}
}
length(even)
# [1] 2
length(even2)
# [1] 2
length(odd)
# [1] 4
length(odd_bad)
# [1] 0
因为通过
<<-
在全局范围内分配变量应该谨慎使用,所以更好的解决方案是从主代码以及用我们使用的标志(例如类)修改的错误处理程序返回一个元素然后可以轻松过滤好的和坏的结果。这完全避免了全局分配:
res <- list()
for (i in 1:2) {
for (j in 1:3) {
res <- c(res,
tryCatch({
stopifnot("i * j must be even" = i * j %% 2 == 0)
list(structure(list(c(i, j)), class = "good"))
}, error = function(err) {
list(structure(list(c(i, j)), class = "bad"))
})
)
}
}
Filter(\(e) inherits(e, "good"), res) ## all good results
Filter(\(e) inherits(e, "bad"), res) ## all badd results