通过附加到新环境来检查 .rdata 文件的内容 - 可能吗?

Posted

技术标签:

【中文标题】通过附加到新环境来检查 .rdata 文件的内容 - 可能吗?【英文标题】:Examining contents of .rdata file by attaching into a new environment - possible? 【发布时间】:2011-07-01 16:15:06 【问题描述】:

我有兴趣在 RDATA 文件中列出对象并仅加载选定的对象,而不是整个集合(以防有些对象可能很大或可能已经存在于环境中)。当名称发生冲突时,我不太清楚如何执行此操作,因为 attach() 不能很好地工作。

1:在不加载 R 数据文件的情况下检查它的内容:这个问题与listing contents of an R data file without loading 提出的问题相似,但不同

在这种情况下,提供的解决方案是:

attach(filename)
ls(pos = 2)
detach()

如果文件中的对象与全局环境中的对象之间存在命名冲突,则会出现以下警告: The following object(s) are masked _by_ '.GlobalEnv':

我尝试创建一个新环境,但我似乎无法融入其中。 例如,这会产生相同的错误:

lsfile   <- function(filename)
  tmpEnv <- new.env()
  evalq(attach(filename), envir = tmpEnv)
  tmpls <- ls(pos = 2)
  detach()
  return(tmpls)

lsfile(filename)

也许我把evalq(或eval)弄得一团糟。有没有其他方法可以避免命名冲突?

2:如果我想访问一个对象——如果没有命名冲突,我可以只使用 .rdat 文件中的那个,或者将它复制到一个新的。如果存在冲突,如何访问文件命名空间中的对象?

例如,如果我的文件是“sample.rdat”,对象是surveyData,并且全局环境中已经存在一个surveyData对象,那么如何从file:sample.rdat命名空间访问该对象?

我目前通过将所有内容加载到临时环境中来解决这个问题,然后将需要的内容复制出来,但这效率低下。

【问题讨论】:

我在 r-devel 上看到了请求,但似乎没有替代加载完整的 .Rdta 文件的方法。 【参考方案1】:

由于刚刚引用了这个问题,让我们澄清两件事:

    attach() 只是调用load(),所以用它代替load 真的没有意义

    如果您想要选择性访问以防止屏蔽,只需将文件加载到新环境中会更容易:

    e = local(load("foo.RData"); environment())
    

    然后您可以使用ls(e) 并访问e$x 等内容。如果你真的想在搜索路径上使用attach,你仍然可以在环境中使用它。

FWIW .RData 文件没有索引(对象存储在一个大的配对列表中),因此您无法在不加载的情况下列出包含的对象。如果您想方便访问,请将其转换为延迟加载格式,而不是简单地添加一个索引,以便可以单独加载每个对象(请参阅Get specific object from Rdata file)

【讨论】:

首先,让我加入欢迎您加入 SO 的合唱团!其次,attach() 调用load() 的事实让我感到惊讶。虽然一个目标是使用attach(),但当我接受另一个答案时,我有一个测试用例(我目前手头没有),其中attach() 似乎比load() 快得多。 OTOH,我现在意识到我可能被磁盘缓存误导了。诅咒。我需要重新审视那个测试用例,看看发生了什么。 嘿,谢谢 - 我看到了关于 SO 的错误信息的链接(我不是指这篇文章),我想我会密切关注它;)。回复attach,我没有检查速度,我只是查看了它的当前R代码:P【参考方案2】:

我只是对load() 使用env= 参数:

> x <- 1; y <- 2; z <- "foo"
> save(x, y, z, file="/tmp/foo.RData")
> ne <- new.env()
> load(file="/tmp/foo.RData", env=ne)
> ls(env=ne)
[1] "x" "y" "z"
> ne$z
[1] "foo"
> 

这种方法的代价是您确实阅读了整个 RData 文件——但另一方面,这似乎是不可避免的,因为似乎没有其他方法可以提供此类文件的“内容”列表。

【讨论】:

如果你讨厌环境混乱,一旦你将感兴趣的东西复制到全局环境中,rm(ne) 可以用来破坏新环境。【参考方案3】:

您可以通过在调用attach 时设置warn.conflicts=FALSE 来抑制警告。如果一个对象在全局环境中被一个对象屏蔽,您可以使用get 从您附加的数据中检索它。

x <- 1:10
save(x, file="x.rData")
#attach("x.rData", pos=2, warn.conflicts=FALSE)
attach("x.rData", pos=2)
(x <- 1)
# [1] 1
(x <- get("x", pos=2))
# [1]  1  2  3  4  5  6  7  8  9 10

【讨论】:

跟进@SimonUrbanek 答案下方留下的评论,似乎我错误地认为attach() 不会调用load()(除非R 已更改:))。该错误是由于缺乏检查(Rprof() 清楚地表明...)和超快的加载时间(可能是由于磁盘缓存)。不过,这回答了我问题的第二部分。正如 Simon 指出的那样,避免基于每次使用加载的唯一方法似乎是一次性转换为延迟加载格式。【参考方案4】:

感谢@Dirk 和@Joshua。

我顿悟了。带有 SMP 或 MC 的命令/包 foreach 似乎生成的环境仅继承全局环境,但似乎不与全局环境冲突。

lsfile   <- function(list_files)
    aggregate_ls = foreach(ix = 1:length(list_files)) %dopar% 
      attach(list_files[ix])
      tmpls <- ls(pos = 2)
      return(tmpls)
    
  return(aggregate_ls)


lsfile("f1.rdat")
lsfile(dir(pattern = "*rdat"))

这对我很有用,因为我现在可以并行化它。这是一个简单的版本,我将对其进行修改以提供更详细的信息,但到目前为止,它似乎是避免冲突的唯一方法,即使没有忽略。

因此,问题 #1 可以通过忽略警告(如 @Joshua 建议的那样)或使用任何魔法 foreach 召唤来解决。

对于第 2 部分,加载一个对象,我认为@Joshua 的想法是正确的——“get”就可以了。

foreach 魔法也可以通过使用.noexport 选项起作用。但是,这有风险:没有明确排除的任何内容都将从全局环境中继承/导出(我可以这样做ls(),但总是有附加数据集的可能性)。为了安全起见,这意味着仍然必须使用get() 以避免命名冲突的风险。加载到子环境中可以避免命名冲突,但不能避免加载不必要的对象。

@Joshua 的回答比我的 foreach 绕道要简单得多。

【讨论】:

以上是关于通过附加到新环境来检查 .rdata 文件的内容 - 可能吗?的主要内容,如果未能解决你的问题,请参考以下文章

通过使用 nmake 文件附加系统时间来重命名 .exe 文件

将 SQL 数据库文件 MDF 和 LDF 移动到新位置

PEiD的工作原理

Ajax 附加的内容选择器没有响应

加载 .RData 文件中包含的对象(如果它们尚不存在)

从数据文件中删除非 ASCII 字符