【问题标题】:Cache expensive operations in R在 R 中缓存昂贵的操作
【发布时间】:2011-03-21 19:33:40
【问题描述】:

一个很简单的问题:

我正在使用文本编辑器编写和运行我的 R 脚本,以使其可重现,正如 SO 的几位成员所建议的那样。

这种方法对我来说效果很好,但有时我必须执行昂贵的操作(例如 read.csvreshape 在 2M 行数据库上),我最好在 R 环境中缓存而不是重新运行每次我运行脚本时(这通常是我进行和测试新代码行的多次)。

有没有办法缓存脚本在某一点之前所做的事情,这样每次我只运行增量代码行(就像我通过交互运行 R 所做的那样)?

谢谢。

【问题讨论】:

    标签: r caching


    【解决方案1】:

    我在使用 Sweave 时也想这样做。我建议将所有昂贵的功能(加载和重塑数据)放在代码的开头。运行该代码,然后保存工作区。然后,注释掉昂贵的函数,并使用load() 加载工作区文件。当然,如果您对工作区文件进行不必要的更改,这会带来更大的风险,但在这种情况下,如果您想从头开始,您仍然可以在 cmets 中使用代码。

    【讨论】:

    • 乔,谢谢你的回答。你知道如何使用 TextMate 保存工作空间吗?
    • load() 方法很好用,它不依赖于 Sweave。
    【解决方案2】:

    一些简单的方法可以通过一些组合来实现

    • exists("foo") 测试变量是否存在,否则重新加载或重新计算
    • 您可以将file.info("foo.Rd")$ctimeSys.time() 进行比较,看看它是否比您可以加载的给定时间更新,否则重新计算。

    CRAN 上还有一些缓存包可能有用。

    【讨论】:

    • Dirk,但不是每次重新运行脚本时都必须重新创建每个对象吗?所以 foo 永远不会存在并且总是被重新计算,对吧?
    • 视情况而定。有时,人们从一个可能很广泛的数据库中获取数据。然后,您可以将其缓存在一个文件中并使用时间戳(如我所述)来查看您是否需要新的数据库访问权限。这完全取决于您的具体情况。
    【解决方案3】:

    没有过多的细节,我通常采用以下三种方法之一:

    1. 使用assign 为我执行过程中的每个重要对象分配一个唯一名称。然后在每个函数的顶部包含一个if(exists(...)) get(...) 以获取值或重新计算它。 (与 Dirk 的建议相同)
    2. cacheSweave 与我的Sweave 文档一起使用。这为您完成了缓存计算并自动检索它们的所有工作。使用起来真的很简单:只需使用 cacheSweave 驱动程序并将这个标志添加到每个块:<<..., cache=true>>=
    3. 在关键时刻使用saveload 来拯救环境,再次确保所有名称都是唯一的。

    【讨论】:

      【解决方案4】:
      ## load the file from disk only if it 
      ## hasn't already been read into a variable
      if(!(exists("mytable")){
        mytable=read.csv(...)
      }
      

      编辑:修正错字 - 感谢 Dirk。

      【讨论】:

      • 感谢 chris,但如何确保表格保存在 TextMate(或其他编辑器)的工作区中?
      • 如果您以非交互方式运行,请使用 save.image(file="mydata.Rdata") 命令保存您的工作区。然后在每次运行开始时使用 load() 加载工作区。由于 R 需要将所有数据恢复到内存中,因此仍会涉及一些磨削,但它会为您节省昂贵的计算步骤。
      • 另外,考虑打开一个 R 会话,在 text mate 中编辑你的脚本,保存它们,然后像​​这样将新代码加载到 R 中: source("~/pathto/myRscript.R") This这样您就不必每次都重新加载数据。结合一些 exists() 语句,它会大大加快速度。
      • mytable 必须以字符串形式给出。正如发布的那样,代码不起作用。
      【解决方案5】:

      在您做了一些您发现成本高昂的事情后,将该高成本步骤的结果保存在 R 数据文件中。

      例如,如果您将 csv 加载到名为 myVeryLargeDataFrame 的数据框中,然后将该数据框的摘要统计信息创建到名为 VLDFSummary 的 df 中,那么您可以这样做:

      save(c(myVeryLargeDataFrame, VLDFSummary), 
        file="~/myProject/cachedData/VLDF.RData", 
        compress="bzip2")
      

      这里的压缩选项是可选的,如果你想压缩正在写入磁盘的文件,可以使用它。有关详细信息,请参阅?save

      保存 RData 文件后,您可以注释掉缓慢的数据加载和摘要步骤以及保存步骤,然后像这样简单地加载数据:

      load("~/myProject/cachedData/VLDF.RData")
      

      此答案不依赖于编辑器。它适用于 Emacs、TextMate 等。您可以保存到计算机上的任何位置。但是,我建议将慢速代码保留在您的 R 脚本文件中,这样您就可以始终知道您的 RData 文件来自何处,并能够在需要时从源数据重新创建它。

      【讨论】:

        【解决方案6】:

        (迟来的答案,但我在这个问题发布一年后开始使用 SO。)

        这是记忆化(或记忆化)背后的基本思想。我有一长串建议,尤其是memoiseR.cache 包,in this query

        您还可以利用检查点,这也是同一列表的一部分。

        我认为您的用例反映了我的第二个用例:“巨大计算的记忆”。 :)

        我使用的另一个技巧是做很多内存映射文件,我经常使用这些文件来存储数据。这样做的好处是多个 R 实例可以访问共享数据,所以我可以让很多实例解决同一个问题。

        【讨论】:

        • 我同意。记忆/缓存(具有可选的持久性)是这里的答案,而不是临时代码。
        【解决方案7】:

        'mustashe' 包非常适合解决此类问题。除了缓存结果外,它还可以包含指向依赖项的链接,以便在依赖项发生变化时重新运行代码。

        披露:我编写了这个工具('mustashe'),尽管我没有从使用它的其他人那里获得任何经济收益。我为自己的工作做了这个确切的目的,并希望与他人分享。

        下面是一个简单的例子。 foo 变量被创建并“隐藏”以备后用。如果重新运行相同的代码,则从磁盘加载foo 变量并添加到全局环境中。

        library(mustashe)
        
        stash("foo", {
            foo <- some_long_running_opperation(1e3)
        }
        #> Stashing object.
        

        该文档包含更复杂用例的其他示例,并详细说明了它的工作原理。

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-13
        • 2020-06-24
        • 1970-01-01
        • 2010-12-15
        • 2013-06-16
        • 1970-01-01
        相关资源
        最近更新 更多