在R中合并工作区或工作空间的管理方式

6
我经常会遇到这样的情况:当一个计算系统出现故障或忙碌时,我需要将工作区转移到不同的临时驱动器中。又或者,我想同时运行两个冗长的程序以节省时间,但是不希望在不同的地方加载同一个工作区两次。
基于此,我非常希望能够找到一种方法来查看不同工作区之间的对象,并将它们合并,只添加新的、更改过的或更新的工作区对象到类似的工作区中。这对我来说将非常有用。
到目前为止,我只能依靠手动记笔记,而且两周后我的笔记可能已经让我感到困惑了。我真的很想学习一些好的工作实践和习惯,使这种操作变得更容易。
总的来说,我真的很想学习更多关于工作区管理的知识,以及有经验的用户如何保持长期项目的工作区全面和整洁。我通常使用Rstudio,但在远程工作或使用我们的HPC系统时,它可能会有点卡顿和笨拙,所以我倾向于使用命令行和交互式会话。
我认为制作对象列表可能是关键,但我希望能够更轻松地注释事物,例如使用的数据和参数等。
谢谢。
3个回答

4

我认为在这里需要构建自己的函数,执行以下操作:

  • loading the workspaces one after one, using:

    load()
    
  • renaming each element of the workspace to prevent overriding when loading another workspace or putting it into a list

  • checking the timestamp of the workspaces with:

    file.info()
    
  • and keeping only the newest objects, which are then to be saved in some up-to-date workspace

例子:

for(i in 1:10){
    dummy <- rnorm(1)
    Sys.sleep(1.3)
    save(dummy,file=paste("test",i,".Rdata",sep=""))
}

DUMMY <- list()
timestamps <- NULL

for(i in 1:10){
    filename <- paste("test",i,".Rdata",sep="")
    load(filename)
    DUMMY[[i]] <- dummy
    timestamps[i] <- file.info(filename)$mtime
}

uptodate <- unlist(timestamps)==max(unlist(timestamps))
dummy <- unlist(DUMMY[uptodate])
save(dummy,file="uptodate.Rdata")

是的,太好了!我不知道file.info。它不能用于工作区内的对象,有没有相应的替代方法?如果有一个简单的方法可以为工作区中的每个文件添加后缀/前缀,那就太好了。我认为这必须在加载之前单独完成?也许如果我能找到一个好的方法,我会再浏览并评论。 - jksl
“[...]有相应的替代品吗?”不,我不认为有。据我所知,对象没有创建/修改属性。也许您可以通过添加一个包含这些信息的对象到工作区(例如通过date())来实现。 - petermeissner

3
我认为关键是将工作区加载到单独的环境中,然后确定如何合并它们(如果需要的话)。
首先,让我们创建一些要保存的对象。
set.seed(1)
a <- data.frame(1:10, 1:10)
b <- rnorm(10)

一种跟踪对象创建时间的方法是设置一个属性。缺点是当更新对象时,您必须记得更新它。(有关替代方案,请参见帖子的最后部分)
d <- structure(data.frame(b), updated=Sys.time())
attr(d, 'updated')
#[1] "2012-10-06 12:34:06 CDT"

你可以在保存工作区之前将当前时间分配给一个变量,以知道何时保存它(file.info可能是更好的替代方案,PeterM 建议
updated <- Sys.time() 
dir.create('~/tmp') # create a directory to save workspace in.
save.image('~/tmp/ws1.RData')

d[1, 1] <- 1 #make a change to `d`
attr(d, "updated") <- Sys.time() # don't forget to update the `updated` attribute
e <- b * a # add a new object
updated <- Sys.time()
save.image('~/tmp/ws2.RData')

现在清空工作区并加载工作区。 但是,不要将它们加载到.GlobalEnv中,而是将它们加载到它们自己的环境中。
rm(list=ls(all=TRUE)) # clear .GlobalEnv
w1 <- new.env()
w2 <- new.env()
load('~/tmp/ws1.RData', envir=w1)
load('~/tmp/ws2.RData', envir=w2)

> ls(w1)
[1] "a"       "b"       "d"       "updated"
> ls(w2)
[1] "a"       "b"       "d"       "e"       "updated"

> with(w1, updated)
[1] "2012-10-06 12:34:09 CDT"
> with(w2, updated)
[1] "2012-10-06 12:35:02 CDT"

> attr(w1$d, 'updated')
[1] "2012-10-06 12:34:06 CDT"
> attr(w2$d, 'updated')
[1] "2012-10-06 12:35:02 CDT"

你可能对类似 .ls.objects 的函数感兴趣。

> .ls.objects(pos=w1)
              Type Size PrettySize Rows Columns
a       data.frame  872    [1] 872   10       2
b          numeric  168    [1] 168   10      NA
d       data.frame 1224   [1] 1224   10       1
updated    POSIXct  312    [1] 312    1      NA
> .ls.objects(pos=w2)
              Type Size PrettySize Rows Columns
a       data.frame  872    [1] 872   10       2
b          numeric  168    [1] 168   10      NA
d       data.frame 1224   [1] 1224   10       1
e       data.frame 1032   [1] 1032   10       2
updated    POSIXct  312    [1] 312    1      NA

您可以使用自定义包装器来跟踪对象何时更新,这需要围绕assign进行操作。
myAssign <- function(x, value, ...) {
  attr(value, "updated") <- Sys.time()
  assign(x, value, ...)
}

> myAssign("b", w1$b[1:2], pos=w1)
> w1$b
[1] -0.6264538  0.1836433
attr(,"updated")
[1] "2012-10-06 12:44:55 CDT"

最后,如果你想要更高级一些,你可以创建一个主动绑定,这样当你的对象发生变化时,它的updated属性总是会得到更新。

f <- local({
  delayedAssign('x', stop('object not found'))
  function(v) {
    if (!missing(v)) x <<- structure(v, updated=Sys.time())
    x
  }
})
makeActiveBinding('ab', f, .GlobalEnv)
> ab # Error, nothing has been assigned to it yet
Error in function (v)  : object not found
> ab <- data.frame(1:10, y=rnorm(10))
> attr(ab, 'updated')
[1] "2012-10-06 12:46:53 CDT"
> ab <- data.frame(10:1, y=rnorm(10))
> attr(ab, 'updated')
[1] "2012-10-06 12:47:04 CDT"

1
+1 其他答案并没有错,但我肯定认为使用环境是实现目标的最清晰方式。 - Dason
我将会逐个示例进行工作。我之前不知道环境的存在。谢谢。 - jksl
1
@jksl,请参考 ?environment?get?assign。此外,您可以使用 as.list 将环境转换为列表,这可能是您更习惯使用的内容。 - GSee

1
我可以回答你问题的一部分,但是其他部分留给 SO 上的其他人来回答。
假设你的工作空间有很多对象,在退出 R 之前,保存工作空间并将其重命名为 .RData,例如 work1.RData。如果你在 Linux 上,尝试使用以下命令重命名文件:
mv .RData work1.RData

然后您打开一个新的R会话,创建任意数量的对象并像以前一样保存它。如果您想将其带到其他系统中,可以重命名此工作区。

现在您有两个工作区.RData二进制文件。您可以使用以下方法将它们加载到单个当前工作区中:

load ("work1.RData")

然后,像这样检查工作区中加载的对象

 ls() 
 objects()

在这种情况下,save.image()也会很有用。

希望对你有所帮助。


谢谢,这与我已经做的相当类似,即在进行足够大的更改或函数运行后,我会按增量保存图像(file="myData_1.X.RData"),但通常大部分对象都是相似的,只有少数被更新、重新运行或新建。我希望R中的ls()有更多的细节可用。如果我在3个不同的地方做了类似的事情,那么将更改的对象组合起来的好方法并不像上面所建议的那样简单。我想我应该在创建对象时将它们写出来,在Linux信息中加上日期戳。 - jksl
刚刚意识到,当您导入保存的对象时,属性不会被保留! - jksl

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