【问题标题】:How to tell what packages you have used in R如何判断你在 R 中使用了哪些包
【发布时间】:2015-04-15 07:29:32
【问题描述】:

我有一个很长的 R 脚本,其中包含许多 if 语句和异常情况。正如我一直在做的那样,如果我一直在导入和测试库,并且还没有真正很好地记录它们。问题是,如果我从全新安装运行它,我不确定脚本将运行哪些语句,以及需要哪些库。

我的问题是:是否有任何 R 函数来测试脚本中正在使用哪些库?

编辑:我没有使用所有已安装的库,所以 print(sessionInfo()) 不会有用,但我只想用 install.packages 函数启动脚本

【问题讨论】:

  • @Ethaan 这不是他真正要问的
  • 我认为您正在寻找脚本作者的铲除/中性工具。我认为你注定要运行脚本并安装包,弄清楚哪个函数来自哪个包。祝你在不同包中使用相同名称的函数好运(这是该工具派上用场的地方)。我发现library("sos");findFn("foo") 可以方便地查找函数。
  • @Ethaan 不用担心,它实际上也可以是一个有用的链接!
  • 我认为应该接受eh21的回答。

标签: r dependencies packages


【解决方案1】:

我不确定自动执行此操作的好方法...但您可以做的是:

  1. 打开一个新的 R 控制台
  2. 请与sessionInfo 确认您没有加载额外的包。
    您可以使用sessionInfo 进行检查。如果您默认加载额外的包(例如,使用您的 .RProfile 文件),我建议您避免这样做,因为这会导致灾难。
    通常你应该只加载基本包:statsgraphicsgrDevicesutilsdatasetsmethodsbase

    您可以使用以下方法卸载任何额外的库:

    detach("package:<packageName>", unload=TRUE)
    
  3. 现在在注释所有 libraryrequire 调用后运行脚本,看看哪些函数会出错。

  4. 在控制台中获取每个函数类型需要哪个包:

    ??<functionName>
    
  5. 加载所需的包并重新运行步骤 3-5,直到满意为止。

【讨论】:

  • 是的,这就是我目前正在做的事情。在开始安装我目前下载的所有软件包时,我可能只会有一些声明。 - 即使它们没有用。
【解决方案2】:

您可能想在 GitHub 上查看来自 Revolution Analytics 的检查点功能:https://github.com/RevolutionAnalytics/checkpoint

它做了一些这样的事情,并解决了可重复性的问题。但我不认为它可以报告您正在使用的列表。

但是,如果您查看代码,您可能会得到一些想法。

【讨论】:

    【解决方案3】:

    如今,“renv”包通过renv::dependencies为此提供了强大的解决方案。

    renv::dependencies 执行适当的静态分析并可靠地找到包依赖关系,即使它们以非标准方式(例如通过box::use)或通过包DESCRIPTION 文件而不是通过library:: 声明.


    作为一个快速破解,我之前(pre-'renv')为此使用了a shell script

    #!/usr/bin/env bash
    
    source_files=($(git ls-files '*.R'))
    grep -hE '\b(require|library)\([\.a-zA-Z0-9]*\)' "${source_files[@]}" | \
        sed '/^[[:space:]]*#/d' | \
        sed -E 's/.*\(([\.a-zA-Z0-9]*)\).*/\1/' | \
        sort -uf \
        > DEPENDS
    

    这使用 Git 来收集项目中版本控制下的所有 R 文件。因为您无论如何都应该使用版本控制,这通常是一个很好的解决方案(尽管您可能想要调整版本控制系统)。对于项目不受版本控制的少数情况,您应该 (1) 将其置于版本控制之下。或者,如果做不到这一点,(2)使用find . -regex '.*\.[rR]' 而不是git ls-files '*.R'

    它会生成一个DEPENDS file,其中包含一个非常简单的依赖项列表。

    不过,它只能找到对 libraryrequire 的直接调用——如果你包装这些调用,脚本将无法工作。

    【讨论】:

    • 我不认为 OP 要求这个,但我可能误解了这个问题。我想他要问的是:他已经加载了几个库,不确定哪个是不必要的。
    • 如果您使用 [\.a-ZA-Z0-9] 而不是 \w[[:alnum:]],您将捕获所有有效的 R 包名称。
    • 这很棒。我们能以某种方式将其放入usethis 吗? (此外,它目前不处理未附加但由::::: 访问的要求,adv-r.had.co.nz/Expressions.html#ast-funs 可能是更通用的基于 R 的实施的良好起点)再说一次,我想我应该使用 roxygen ...
    • @jan-glx 老实说,我今天不会使用这个 sn-p。如果我必须自己实现这个,我会做一个适当的静态分析,可能基于“codetools”包。也就是说,'renv' 已经实现了这一点,并且做得好多,因为它还支持声明包依赖项的非标准方式(例如通过box::use)。我已更新我的答案以反映这一点。
    【解决方案4】:

    当我需要将我的代码转换为一个包时,我也有类似的需求,因此我需要识别每个包依赖项并导入或使用完整的限定名。

    在看书Extending R我发现XRtools::makeImports可以扫描一个包,发现所有需要导入的包。这还不能解决我们的问题,因为它只适用于现有的包,但它提供了关于如何做到这一点的主要见解。

    我做了一个函数并将它放入我的包mischelper。您可以安装软件包,使用 RStudio 插件菜单扫描当前文件或选定代码,或使用命令行功能。每个外部函数(fun_inside)和调用它的函数(用法)都会列在表中。

    您现在可以转到每个功能,按 F1 查找它属于哪个包。我实际上还有另一个包,它可以扫描所有已安装包的函数名称并构建数据库,但这可能会导致更多误报,因为如果您只加载了一些包,按 F1 只会搜索已加载的包。

    在我的包页面查看使用详情

    https://github.com/dracodoc/mischelper

    【讨论】:

      【解决方案5】:

      我发现 NCmisc (install.packages("NCmisc")) 的 list.functions.in.file() 函数对此非常有帮助:

      list.functions.in.file(filename, alphabetic = TRUE)

      欲了解更多信息,请参阅此链接:https://rdrr.io/cran/NCmisc/man/list.functions.in.file.html

      【讨论】:

      • 为什么这被否决了?这不是首选选项有什么原因吗?
      • 请注意——你需要先加载包,否则NCmisc不知道函数来自哪个包。
      • 如果你在使用 RStudio 并且想用它来检查你打开的脚本,运行list.functions.in.file(rstudioapi::getSourceEditorContext()$path, alphabetic = TRUE)
      • 这种方法的一个问题是它实际上并没有关注包的加载顺序,所以它会显示函数来自多个包,而在脚本的现实中它会是来自一个特定的。你知道任何可以更好地解决这个问题的替代方案吗?
      • 我写了一个包 {funspotr},它本质上是 list.functions.in.file,但以数据框格式输出内容并进行一些其他小的更改:github.com/brshallo/funspotr
      【解决方案6】:

      根据大家的反应,尤其是eh21NCmisc包的建议,我整理了一个小函数,输出一个目录中所有R脚本中使用的包的列表,以及它们的频率。

      library(NCmisc)
      library(stringr)
      library(dplyr)
      
      checkPacks<-function(path){
      
          ## get all R files in your directory
          ## by the way, extract R code from Rmd: http://felixfan.github.io/extract-r-code/
          files<-list.files(path)[str_detect(list.files(path), ".R$")]
      
          ## extract all functions and which package they are from 
          ## using NCmisc::list.functions.in.file
          funs<-unlist(lapply(paste0(path, "/", files), list.functions.in.file))
          packs<-funs %>% names()
      
          ## "character" functions such as reactive objects in Shiny
          characters<-packs[str_detect(packs, "^character")]
      
          ## user defined functions in the global environment
          globals<-packs[str_detect(packs, "^.GlobalEnv")]
      
          ## functions that are in multiple packages' namespaces 
          multipackages<-packs[str_detect(packs, ", ")]
      
          ## get just the unique package names from multipackages
          mpackages<-multipackages %>%
                     str_extract_all(., "[a-zA-Z0-9]+") %>%
                     unlist() %>%
                     unique()
          mpackages<-mpackages[!mpackages %in% c("c", "package")]
      
          ## functions that are from single packages
          packages<-packs[str_detect(packs, "package:") & !packs %in% multipackages] %>%
                    str_replace(., "[0-9]+$", "") %>%
                    str_replace(., "package:", "") 
      
          ## unique packages
          packages_u<-packages %>%
                      unique() %>%
                      union(., mpackages)
      
          return(list(packs=packages_u, tb=table(packages)))
      
      }
      
      checkPacks("~/your/path")
      

      【讨论】:

      • Nice 很好用,但它只检查加载的库。一种解决方案是加载此博客文章中描述的所有已安装包:r-bloggers.com/loading-all-installed-r-packages 短:lapply(.packages(all.available = TRUE), function(xx) library(xx, character.only = TRUE))
      • 很好,但你应该让第一个 list.files-call 中的正则表达式为 ".R$|.r$" 以便也使用带有 .r 的文件(像我一样 - 我从不使用与编程相关的文件夹中的大写字母。
      • 您也可以使用正则表达式的模式参数而不是字符串,如下所示:
      • 文件
      【解决方案7】:

      我最信任基于 {renv} 的解决方案来识别包依赖关系。

      虽然我编写了一个包funspotr,其中包含与提到NCmisc::list.functions.in.file() 的答案类似的功能,并且可用于解析一个或多个文件中的函数或包:

      library(dplyr)
      
      funspotr::spot_pkgs("https://gist.githubusercontent.com/brshallo/4b8c81bc1283a9c28876f38a7ad7c517/raw/b399b768e900a381d99f5120e44d119c7fb40ab9/source_rmd.R")
      #> [1] "knitr"    "magrittr" "stringr"  "readr"    "purrr"    "glue"
      
      funspotr::spot_funs("https://gist.githubusercontent.com/brshallo/4b8c81bc1283a9c28876f38a7ad7c517/raw/b399b768e900a381d99f5120e44d119c7fb40ab9/source_rmd.R") %>% 
        select(-in_multiple_pkgs)
      #> # A tibble: 13 x 2
      #>    funs        pkgs   
      #>    <chr>       <chr>  
      #>  1 tempfile    base   
      #>  2 purl        knitr  
      #>  3 getOption   base   
      #>  4 options     base   
      #>  5 .Call       base   
      #>  6 source      base   
      #>  7 library     base   
      #>  8 read_file   readr  
      #>  9 map         purrr  
      #> 10 str_extract stringr
      #> 11 glue        glue   
      #> 12 str_c       stringr
      #> 13 write_file  readr
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多