为什么函数 `load` 在 `lapply` 中不起作用,但在 `for` 循环中起作用?

Why function `load` not works in `lapply` but works in `for` loops?

我正在尝试将一系列文件加载到 R 中的列表中。下面是示例和我使用的代码。

## data
val <- c(1:5)
save(val, file='test1.rda')
val <- c(6:10)
save(val, file='test2.rda')

## file names
files = paste0('test',c(1:2), '.rda')
# "test1.rda" "test2.rda"

## use apply to load data into a list 
res <- lapply(files, function(x) load(x))
res
# [[1]]
# [1] "val" # ??? supposed to be 1,2,3,4,5
# 
# [[2]]
# [1] "val" # ??? supposed to be 6,7,8,9,10


## use for loops to load data
for (i in c(1:2)){
  load(files[i])
}
# data sets are loaded as expected

我不明白为什么 apply + load 函数没有返回正确的列表。如果有人能指出正确的方向,我将不胜感激。

load 函数不是分配保存的 R 对象的好方法,因为它直接在您的环境中加载对象(就像您在 for 循环中所做的那样,而不分配新的命名对象)

saveRDSreadRDS 将帮助您将保存的文件分配给您环境中的新对象;

val <- c(1:5)
saveRDS(val, file='test1.rds')
val <- c(6:10)
saveRDS(val, file='test2.rds')

files = paste0('test',c(1:2), '.rds')

res <- lapply(files, function(x) readRDS(x))
res

输出;

1. 1 2 3 4 5
2. 6 7 8 9 10

前面的底线:load 将数据加载到调用环境中,当 运行 来自 for 循环和来自 lapply 时,这是非常不同的。您可以覆盖它以强制进入加载数据的环境。

如果您阅读 ?load,您将看到 envir= 参数:

Usage:

     load(file, envir = parent.frame(), verbose = FALSE)
     
Arguments:

    file: a (readable binary-mode) connection or a character string
          giving the name of the file to load (when tilde expansion is
          done).

   envir: the environment where the data should be loaded.

 verbose: should item names be printed during loading?

由于默认值为 parent.frame(),这意味着它被加载到 lapply 中定义的环境中,而不是全局环境。

示范:

for (i in 1:2) { print(environment()); }
# <environment: R_GlobalEnv>
# <environment: R_GlobalEnv>
ign <- lapply(1:2, function(ign) print(environment()))
# [[1]]
# <environment: 0x000000006f54b838>                # not R_GlobalEnv, aka .GlobalEnv
# [[2]]
# <environment: 0x000000006f54de58>

此外,由于

Value:

     A character vector of the names of objects created, invisibly.

这意味着 res <- lapply(files, load) 将始终只 return 一个 character 向量,而不是值本身。

虽然我同意 Samet Sökel 的前提,即 readRDS 提供了一个更具 功能的 界面(意思是:它 return 是某种东西,但它无法运行仅考虑副作用),解决方法并不难:

  1. 载入全局环境:

    res <- lapply(files, load, envir = .GlobalEnv)
    

    这将return加载到res中的所有变量的名称,以及出现在全局环境中的所有数据。

  2. 加载到用户定义的环境中:

    e <- new.env(parent = emptyenv())
    res <- lapply(files, load, envir = e)
    # all data is now in 'e'
    

    res 也将仅包含名称,但这更接近于 功能性 接口,因为数据将进入一个非常具体的位置定义。

    不要很快忽略这一点:如果您选择“生产化”加载所有 .rda 文件的代码,那么将数据加载到除 .GlobalEnv。其一,在函数内部加载并将数据放在全局中是非常糟糕的做法,并且它可能并不总是能顺利地为您的函数工作。好吧,它只是“一个”,生产类型 function/package 中的副作用是一件坏事(imo):它经常破坏可重复性,它真的会让碰巧在 他们的 环境...覆盖它们是不可逆的操作,会很快导致愤怒和生产力下降。当出现问题时,副作用也很难解决。