我有一个非常长的R脚本,其中包含许多if语句和异常情况。在编写脚本时,我一边导入和测试库,一边进行操作,并没有很好地记录它们。问题是,如果我从干净的安装中运行此脚本,我不确定脚本将运行哪些语句,因此需要哪些库。
我的问题是:是否有任何R函数可以测试脚本中正在使用的库?
编辑:我并没有使用已安装的所有库,因此print(sessionInfo())
不会有用,我只想使用install.packages
函数启动脚本
我有一个非常长的R脚本,其中包含许多if语句和异常情况。在编写脚本时,我一边导入和测试库,一边进行操作,并没有很好地记录它们。问题是,如果我从干净的安装中运行此脚本,我不确定脚本将运行哪些语句,因此需要哪些库。
我的问题是:是否有任何R函数可以测试脚本中正在使用的库?
编辑:我并没有使用已安装的所有库,因此print(sessionInfo())
不会有用,我只想使用install.packages
函数启动脚本
我发现NCmisc中的list.functions.in.file()
函数(install.packages("NCmisc")
) 对于这个问题非常有帮助:
list.functions.in.file(文件名, alphabetic = TRUE)
更多信息请查看此链接: https://rdrr.io/cran/NCmisc/man/list.functions.in.file.html
list.functions.in.file(rstudioapi::getSourceEditorContext()$path, alphabetic = TRUE)
。 - Matthew Law如今,'renv'软件包通过renv::dependencies
提供了一个强大的解决方案。
renv::dependencies
通过正确的静态分析,可靠地找到软件包依赖关系,即使它们以非标准的方式声明(例如通过box::use
)或通过软件包DESCRIPTION
文件而不是通过library
或::
。
作为一个快速的hack,在“renv”之前,我曾经使用一个shell脚本来进行此操作:
#!/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
文件,其中包含非常简单的依赖关系列表。
但是要注意它只查找直接调用library
和require
,如果您将这些调用包装起来,则脚本将无法正常工作。
[\.a-zA-Z0-9]
而不是\w
和[[:alnum:]]
,则可以捕获所有有效的R软件包名称。 - calder-tyusethis
中?(此外,它目前无法处理未附加但通过 ::
或 :::
访问的要求,http://adv-r.had.co.nz/Expressions.html#ast-funs 可能是更一般的、基于 R 的实现的良好起点)不过,我想我应该使用 roxygen... - jan-glxbox::use
)。我已更新我的答案以反映这一点。 - Konrad Rudolph根据大家的反应,特别是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")
lapply(.packages(all.available = TRUE), function(xx) library(xx, character.only = TRUE))
- Sebastian Müller我不确定有一个好的自动化方法...但你可以这样做:
Check with sessionInfo
that you don't have extra packages loaded.
You could check this using sessionInfo
. If you, by default, load extra packages (e.g. using your .RProfile file) I suggest you avoid doing that, as it's a recipe for disaster.
Normally you should only have the base packages loaded: stats
, graphics
, grDevices
, utils
, datasets
, methods
, and base
.
You can unload any extra libraries using:
detach("package:<packageName>", unload=TRUE)
Now run the script after commenting all of the library
and require
calls and see which functions give an error.
To get which package is required by each function type in the console:
??<functionName>
Load the required packages and re-run steps 3-5 until satisfied.
您可能希望查看 Revolution Analytics 在 GitHub 上的 checkpoint 功能,链接为:https://github.com/RevolutionAnalytics/checkpoint
该功能可以解决一些问题,并确保可重复性。但是我认为它无法报告您正在使用的列表。
不过,如果您查看代码,可能会得到一些想法。
对于识别软件包依赖项,我最信任基于{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
library("sos");findFn("foo")
很方便查找函数。 - Roman Luštrik