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

Ite*_*tor 24 dependencies code-analysis r

我正在筛选使用该包的包和脚本,并希望识别外部依赖项.目标是修改脚本以指定library(pkgName)和修改要使用的包中的函数require(pkgName),以便稍后这些依赖性将更加明显.

我正在修改代码以考虑每个外部相关的包.作为一个例子,虽然它绝不是决定性的,但我现在发现很难识别依赖的代码data.table.我可以取代data.tableMatrix,ggplot2,bigmemory,plyr,或许多其他包,所以随时与基于其他包的例子来回答.

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

  • 搜索代码libraryrequire语句
  • 搜索data.table(例如library(data.table))的提及
  • 尝试运行codetools::checkUsage以确定可能存在某些问题的位置.对于脚本,我的程序将脚本插入本地函数并应用于checkUsage该函数.否则,我checkUsagePackage用于包.
  • 寻找有些独特的陈述data.table,例如:=.
  • 查找可通过匈牙利表示法识别对象类的位置,例如 DT

我搜索的本质是找到:

  • 装载data.table,
  • 名称表明它们是data.table对象的对象,
  • 似乎是data.table特定的方法

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

这(data.table)只是一个包,一个似乎是有限的和一些独特的用法.假设我想寻找ggplot函数的用法,其中选项更广泛,并且语法的文本不是特殊的(即频繁使用+不是特殊的,而:=似乎是).

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

对于它的价值,tools::pkgDepends只能解决包级别的依赖关系,而不是函数或脚本级别,这是我正在处理的级别.


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

Ite*_*tor 21

首先,感谢@ mathematicalcsoffee让我走上了使用Mark Bravington的mvbutils包的道路.该foodweb功能是不是满意了.

回顾一下,我想知道关于检查一个包,比如说myPackage另一个包,说externalPackage,以及关于检查脚本externalPackage.我将演示如何做每一个.在这种情况下,外部包是data.table.

1:对于myPackagevs 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)
Run Code Online (Sandbox Code Playgroud)

这样可以生成一个很好的图表,显示哪些函数依赖于函数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,]
Run Code Online (Sandbox Code Playgroud)

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

注意: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)
}
Run Code Online (Sandbox Code Playgroud)

现在,我只需要看一下listDeps,我就可以从上面的depMat获得同样精彩的小见解.我修改checkScriptDependencies了我编写的其他代码,发送脚本进行分析codetools::checkUsage; 有一个像这样的小功能来分析独立代码是很好的.荣誉对@Spacedman@Tommy为提高调用的见解foodweb,使用environment().

(真正的匈牙利人会注意到我与名称和类型的顺序不一致 - tooBad.:)这有更长的原因,但这不正是我正在使用的代码.无论如何.)


虽然我没有发布foodweb为我的代码生成的图表的图片,但你可以在http://web.archive.org/web/20120413190726/http://www.sigmafield.org/2010/09上看到一些不错的例子./21/r-day-the-day-foodweb.在我的情况下,它的输出肯定捕获data.table的使用:=J,以及标准的命名函数,如keyas.data.table.它似乎可以避免我的文本搜索,并且是几种方式的改进(例如,找到我忽略的功能).

总而言之,这foodweb是一个很好的工具,我鼓励其他人去探索mvbutils包装和一些Mark Bravington的其他不错的包装,比如debug.如果您确实安装了mvbutils,只需查看?changed.funs您是否认为只有您在管理不断发展的R代码时才会遇到困难.:)