识别 R 函数和脚本的依赖关系

2024-03-08

我正在筛选一个包和使用该包的脚本,并希望识别外部依赖项。目标是修改脚本以指定library(pkgName)并修改包中的函数以使用require(pkgName),这样以后这些依赖关系就会更加明显。

我正在修改代码以考虑每个外部依赖包。举个例子,虽然它绝不是确定的,但我现在发现很难识别依赖于data.table。我可以替换data.table with Matrix, ggplot2, bigmemory, plyr或许多其他软件包,因此请随意回答基于其他软件包的示例。

这个搜索并不是特别容易。到目前为止我尝试过的方法包括:

  • 搜索代码library and require声明
  • 搜索提及data.table (e.g. library(data.table))
  • 尝试跑步codetools::checkUsage以确定哪里可能存在问题。对于脚本,我的程序将脚本插入本地函数并应用checkUsage到该功能。否则,我用checkUsagePackage对于包裹。
  • 寻找一些独特的陈述data.table, 例如:=.
  • 寻找可以通过匈牙利表示法识别对象类别的位置,例如DT

我搜索的本质是找到:

  • 装载的data.table,
  • 具有表明它们是的名称的对象data.table物体,
  • 看起来是的方法data.table-具体的

唯一简单的部分似乎是找到包的加载位置。不幸的是,并非所有函数都可以显式加载或需要外部包 - 这些函数可能假设它已经被加载。这是一个不好的做法,我正在努力解决它。然而,寻找对象和方法似乎具有挑战性。

This (data.table)只是一个包,并且是一个看起来有限且有些独特用途的包。假设我想寻找 ggplot 函数的用途,其中选项更广泛,并且语法文本不那么特殊(即频繁使用+并不特殊,同时:=似乎是)。

我不认为静态分析会给出完美的答案,例如可以将参数传递给函数,该函数指定要加载的包。尽管如此:是否有任何核心工具或软件包可以通过静态或动态分析来改进这种强力方法?

物有所值,tools::pkgDepends仅解决包级别的依赖关系,而不是函数或脚本级别的依赖关系,这是我正在工作的级别。


更新 1:应该起作用的动态分析工具的一个示例是报告在代码执行期间加载了哪些包的工具。不过,我不知道 R 中是否存在这样的功能 - 它就像Rprof报告输出search()而不是代码堆栈。


首先,感谢 @mathematical.coffee 让我走上了使用 Mark Bravington 的道路mvbutils包裹。这foodweb功能还是比较满意的。

回顾一下,我想了解如何检查一个包裹,比如说myPackage与另一个相比,说externalPackage,以及关于检查脚本externalPackage。我将演示如何进行每一项操作。在这种情况下,外部包是data.table.

1: For myPackage versus data.table,以下命令就足够了:

library(mvbutils)
library(myPackage)
library(data.table)
ixWhere <- match(c("myPackage","data.table"), search())
foodweb(where = ixWhere, prune = ls("package:data.table"), descendents = FALSE)

这会生成一个出色的图表,显示哪些函数依赖于中的函数data.table。尽管图中包含了依赖关系data.table,这并不太繁重:我可以很容易地看到我的哪些功能依赖于data.table,以及他们使用哪些函数,例如as.data.table, data.table, :=, key, 等等。至此,可以说包依赖问题解决了,但是foodweb提供了更多内容,让我们来看看。最酷的部分是依赖矩阵。

depMat  <- foodweb(where = ixWhere, prune = ls("package:data.table"), descendents = FALSE, plotting = FALSE)
ix_sel  <- grep("^myPackage.",rownames(depMat))
depMat  <- depMat[ix_sel,]
depMat  <- depMat[,-ix_sel]
ix_drop <- which(colSums(depMat) == 0)
depMat  <- depMat[,-ix_drop]
ix_drop <- which(rowSums(depMat) == 0)
depMat  <- depMat[-ix_drop,]

这很酷:它现在显示了我的包中函数的依赖关系,我在其中使用了详细的名称,例如myPackage.cleanData,关于函数不 在我的包中,即函数data.table,并且它消除了没有依赖性的行和列。这很简洁,让我可以快速调查依赖关系,并且通过处理,我也可以很容易地找到我的函数的补充集rownames(depMat).

NB: plotting = FALSE似乎并没有阻止绘图设备的创建,至少在第一次foodweb在一系列调用中被调用。这很烦人,但并不可怕。也许我做错了什么。

2:对于脚本与data.table,这变得更有趣了。对于每个脚本,我需要创建一个临时函数,然后检查依赖关系。我下面有一个小函数可以做到这一点。

listFiles <- dir(pattern = "myScript*.r")
checkScriptDependencies <- function(fname){
    require(mvbutils)
    rawCode  <- readLines(fname)
    toParse  <- paste("localFunc <- function(){", paste(rawCode, sep = "\n", collapse = "\n"), "}", sep = "\n", collapse = "")
    newFunc  <- eval(parse(text = toParse))
    ix       <- match("data.table",search())
    vecPrune <- c("localFunc", ls("package:data.table"))
    tmpRes   <- foodweb(where = c(environment(),ix), prune = vecPrune, plotting = FALSE)
    tmpMat   <- tmpRes$funmat
    tmpVec   <- tmpMat["localFunc",]
    return(tmpVec)
}

listDeps <- list()
for(selFile in listFiles){
    listDeps[[selFile]] <- checkScriptDependencies(selFile)
}

现在,我只需要看看listDeps,我也有与上面的 depMat 相同的奇妙的小见解。我修改了checkScriptDependencies来自我编写的发送脚本进行分析的其他代码codetools::checkUsage;有一个像这样的小函数来分析独立代码是很好的。感谢@太空人 https://chat.stackoverflow.com/transcript/message/2307138#2307138 and @Tommy https://stackoverflow.com/a/8773047/805808寻求改善通话的见解foodweb, using environment().

(真正的匈牙利人会注意到我与名称和类型的顺序不一致 - 太糟糕了。:) 这样做有一个更长的原因,但这并不完全是我正在使用的代码。)


虽然我还没有发布生成的图表的图片foodweb对于我的代码,您可以在以下位置看到一些很好的示例http://web.archive.org/web/20120413190726/http://www.sigmafield.org/2010/09/21/r-function-of-the-day-foodweb http://web.archive.org/web/20120413190726/http://www.sigmafield.org/2010/09/21/r-function-of-the-day-foodweb。就我而言,它的输出肯定捕获了 data.table 的用法:= and J,以及标准命名函数,例如key and as.data.table。它似乎避免了我的文本搜索,并且在几个方面都有所改进(例如查找我忽略的功能)。

总而言之,foodweb是一个很棒的工具,我鼓励其他人探索mvbutils包和 Mark Bravington 的其他一些不错的包,例如debug。如果你安装了mvbutils,只需查看?changed.funs如果您认为只有您在管理不断发展的 R 代码方面遇到困难。 :)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

识别 R 函数和脚本的依赖关系 的相关文章

随机推荐