【问题标题】:hiding personal functions in R在R中隐藏个人功能
【发布时间】:2011-06-17 05:14:47
【问题描述】:

我的 .Rprofile 中有一些便利功能,例如 handy function for returning the size of objects in memory。有时我喜欢在不重新启动的情况下清理我的工作区,我使用rm(list=ls()) 执行此操作,这会删除我所有用户创建的对象和我的自定义函数。我真的不想破坏我的自定义功能。

解决此问题的一种方法似乎是使用我的自定义函数创建一个包,以便我的函数最终位于它们自己的命名空间中。这不是特别难,但是有没有更简单的方法来确保自定义函数不会被 rm() 杀死?

【问题讨论】:

    标签: r namespaces


    【解决方案1】:

    attachsys.source 组合到一个环境中并附加该环境。这里我在文件my_fun.R中有两个函数:

    foo <- function(x) {
        mean(x)
    }
    
    bar <- function(x) {
        sd(x)
    }
    

    在我加载这些函数之前,显然没有找到:

    > foo(1:10)
    Error: could not find function "foo"
    > bar(1:10)
    Error: could not find function "bar"
    

    创建一个环境并将文件导入其中:

    > myEnv <- new.env()
    > sys.source("my_fun.R", envir = myEnv)
    

    仍然不可见,因为我们没有附加任何东西

    > foo(1:10)
    Error: could not find function "foo"
    > bar(1:10)
    Error: could not find function "bar"
    

    当我们这样做时,它们是可见的,并且因为我们已将环境的副本附加到搜索路径,所以函数在rm()-ed 中仍然存在:

    > attach(myEnv)
    > foo(1:10)
    [1] 5.5
    > bar(1:10)
    [1] 3.027650
    > rm(list = ls())
    > foo(1:10)
    [1] 5.5
    

    我仍然认为您最好使用自己的个人包裹,但同时上述内容可能就足够了。请记住,搜索路径上的副本就是副本。如果函数相当稳定并且您没有编辑它们,那么上面的方法可能有用,但如果您正在开发和修改函数,这可能比它的价值更麻烦。

    第二种选择是将它们全部命名为.foo 而不是foo,因为ls() 不会返回这样命名的对象,除非设置了参数all = TRUE

    > .foo <- function(x) mean(x)
    > ls()
    character(0)
    > ls(all = TRUE)
    [1] ".foo"         ".Random.seed"
    

    【讨论】:

    • 个人包对此很好,尽管我仍然很烦你必须跳过这么多圈来构建一个包。为什么我必须为个人包中的每个功能提供文档?
    • 可能是因为我们不希望 CRAN 上出现未记录的包,如果 R Core 允许绕过某些检查,他们必须编写一大堆代码才能允许安装并加载一个有缺陷的包。提供了一些用户工具来帮助编写非正式的包——比如 roxygen——所以你维护一个源文件(没有 Rd 文件)并从中生成包文件。
    • 而且您不必记录每个函数。只需将每个函数的 \alias{} 粘贴到单个帮助文件中,就足以使检查失败。你不需要 \usage{} 部分等,所以不要提供它们。在 NAMESPACES 被大量使用之前,这个技巧经常用于内部包函数。
    • @Gavin 当前的块不会阻止出现在 CRAN 上的未记录包。正如您所指出的,您可以提供它为空的文档。人们记录他们的工作,因为没有它发表将毫无意义。在我看来,如果希望在 CRAN 上满足某些标准,那么它们应该在进入 CRAN 时强制执行。当前的保姆方法只是对用户怀有敌意。
    • @Gavin 现在,这在您正在开发和重构现有代码的情况下很重要。是的,您可能使用了 \alias{} 技巧,但是如果您重命名和重构一堆函数会怎样。然后,您需要更新文档以匹配。然后您发现它不能满足您的需求,因此您尝试了其他方法,并且不得不再次更改文档。这些干扰使开发工作变得更加困难,我是以开发人员而不是统计学家的身份发言。
    【解决方案2】:

    与 Gavin 的回答类似,以下内容会加载一个函数文件,但不会留下额外的环境对象:

    if('my_namespace' %in% search()) detach('my_namespace'); source('my_functions.R', attach(NULL, name='my_namespace')) 
    

    如果已附加命名空间的旧版本(对开发有用),这将删除旧版本,然后将一个名为 my_namespace 的空新环境和源 my_functions.R 附加到其中。如果您不删除旧版本,您将构建多个同名的附加环境。

    如果您想查看已加载哪些函数,请查看输出

    ls('my_namespace')
    

    要卸载,请使用

    detach('my_namespace')
    

    这些附加的函数,就像一个包一样,不会被rm(list=ls())删除。

    【讨论】:

      【解决方案3】:

      这里有两种方法:

      1) 让每个函数名称都以点开头。例如.f 而不是 fls 不会列出此类函数,除非您使用 ls(all.names = TRUE),因此它们不会传递给您的 rm 命令。

      或者,

      2) 把它放在你的 .Rprofile 中

      attach(list(
         f = function(x) x, 
         g = function(x) x*x
      ), name = "MyFunctions")
      

      这些函数将作为一个名为 "MyFunctions" 的组件出现在您的搜索列表中,而不是在您的工作区中,并且几乎可以像在您的工作区中一样访问它们。 search() 将显示您的搜索列表,ls("MyFunctions") 将列出您附加的函数的名称。由于它们不在您的工作区中,您通常使用的rm 命令不会删除它们。如果您确实希望删除它们,请使用detach("MyFunctions")

      【讨论】:

        【解决方案4】:

        第一个快速而肮脏的选择是在使用rm() 时使用lsf.str(),以获取当前工作区中的所有功能。 ...并让您根据需要命名函数。

        pattern <- paste0('*',lsf.str(), '$', collapse = "|")
        rm(list = ls()[-grep(pattern, ls())])
        

        我同意,这可能不是最佳做法,但它可以完成工作! (而且我必须有选择性地自己清理......)

        【讨论】:

          【解决方案5】:

          我发现当我创建或调试一个函数时,我的 R 环境经常会被各种对象弄得一团糟。我想要一种方法来有效地保持环境中没有这些物体,同时保留个人功能。

          下面的简单功能是我的解决方案。它做了两件事: 1) 删除所有不以大写字母开头的非函数对象,然后 2) 将环境保存为 RData 文件

          (需要 R.oo 包)

          cleanup=function(filename="C:/mymainR.RData"){  
          library(R.oo)  
          # create a dataframe listing all personal objects
          everything=ll(envir=1)
          #get the objects that are not functions
          nonfunction=as.vector(everything[everything$data.class!="function",1])
          #nonfunction objects that do not begin with a capital letter should be deleted
          trash=nonfunction[grep('[[:lower:]]{1}',nonfunction)]
          remove(list=trash,pos=1)
          #save the R environment
          save.image(filename)
          print(paste("New, CLEAN R environment saved in",filename))
          }
          

          要使用此功能,必须始终遵守 3 条规则:
          1) 将所有数据保留在 R 外部。
          2) 对我希望永久可用的非功能对象使用以大写字母开头的名称。
          3) 过时的功能必须用 rm 手动删除。

          显然,这不是对每个人都适用的通用解决方案……如果您不遵守规则 #1 和 #2,则可能会造成灾难性的后果。但它确实有很多优点:a) 担心我的数据会被 cleanup() 破坏,这让我自律地只使用 R 作为处理器而不是数据库,b) 我的主要 R 环境非常小,我可以备份为电子邮件附件, c) 自动保存新功能(我不必手动管理个人功能列表)和 d) 保留对现有功能的所有修改。当然,最好的优势是最明显的……我不必花时间做 ls() 和审查对象来决定它们是否应该被 rm'd。

          即使你不关心我的系统的细节,R.oo 中的“ll”功能对于这类事情也非常有用。它可用于实现几乎任何符合您个人编程风格的清理规则。

          帕特里克·莫尔

          【讨论】:

            【解决方案6】:

            另一种选择:将函数保存在来自.RProfile 的单独文件中。您可以在闲暇时直接从 R 中重新获取内容。

            【讨论】:

              【解决方案7】:

              Gavin 的回答很精彩,我刚刚投了赞成票。只是为了完整,让我再扔一个:

              R> q("no")
              

              紧随其后

              M-x R
              

              创建一个新会话——它重新读取.Rprofile。简单、快速、便宜。

              除此之外,私人包是我书中的方式。

              【讨论】:

              • 假设一个在 ESS 中。我是谁。除非我不在。
              • 但是它是一个 shell 提示,我们重新启动 littler。更快:)
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-06-01
              • 1970-01-01
              • 2017-10-13
              • 2012-09-23
              • 2010-09-17
              • 2010-09-13
              • 2010-09-23
              相关资源
              最近更新 更多