如何判断你在 R 中使用了哪些包
Posted
技术标签:
【中文标题】如何判断你在 R 中使用了哪些包【英文标题】:How to tell what packages you have used in R 【发布时间】:2015-04-15 07:29:32 【问题描述】:我有一个很长的 R 脚本,其中包含许多 if 语句和异常情况。正如我一直在做的那样,如果我一直在导入和测试库,并且还没有真正很好地记录它们。问题是,如果我从全新安装运行它,我不确定脚本将运行哪些语句,以及需要哪些库。
我的问题是:是否有任何 R 函数来测试脚本中正在使用哪些库?
编辑:我没有使用所有已安装的库,所以 print(sessionInfo())
不会有用,但我只想用 install.packages
函数启动脚本
【问题讨论】:
this is what are you looking for @Ethaan 这不是他真正要问的 我认为您正在寻找脚本作者的铲除/中性工具。我认为你注定要运行脚本并安装包,弄清楚哪个函数来自哪个包。祝你在不同包中使用相同名称的函数好运(这是该工具派上用场的地方)。我发现library("sos");findFn("foo")
可以方便地查找函数。
@Ethaan 不用担心,它实际上也可以是一个有用的链接!
我认为应该接受eh21的回答。
【参考方案1】:
我不确定自动执行此操作的好方法...但您可以做的是:
-
打开一个新的 R 控制台
请与sessionInfo
确认您没有加载额外的包。
您可以使用sessionInfo
进行检查。如果您默认加载额外的包(例如,使用您的 .RProfile 文件),我建议您避免这样做,因为这会导致灾难。
通常你应该只加载基本包:stats
、graphics
、grDevices
、utils
、datasets
、methods
和base
。
您可以使用以下方法卸载任何额外的库:
detach("package:<packageName>", unload=TRUE)
现在在注释所有 library
和 require
调用后运行脚本,看看哪些函数会出错。
在控制台中获取每个函数类型需要哪个包:
??<functionName>
加载所需的包并重新运行步骤 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,其中包含一个非常简单的依赖项列表。
不过,它只能找到对 library
和 require
的直接调用——如果你包装这些调用,脚本将无法工作。
【讨论】:
我不认为 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】:
根据大家的反应,尤其是eh21对NCmisc包的建议,我整理了一个小函数,输出一个目录中所有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
【讨论】:
以上是关于如何判断你在 R 中使用了哪些包的主要内容,如果未能解决你的问题,请参考以下文章
如何运行使用旧版本包和最新 R 版本和包中的 R 编写的 R 脚本? [关闭]