我正在修改代码以解决每个外部依赖的包。例如,尽管这绝对不是确定性的,但我现在发现难以识别依赖于data.table的代码。我可以用Matrix,ggplot2,bigmemory,plyr或许多其他软件包替换data.table,所以随便回答一下基于其他软件包的示例。
>搜索库的代码和需要的语句
>搜索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()的输出而不是代码堆栈。
要概述,我想知道关于检查一个包,说myPackage与另一个包,说externalPackage,以及关于根据externalPackage检查脚本。我将演示如何做到这一点。在这种情况下,外部包是data.table。
1:对于myPackage与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,关键等等。在这一点上,可以说包依赖问题解决了,但是foodweb提供了更多的东西,所以让我们来看看。酷的部分是依赖矩阵。
depMat <- foodweb(where = ixWhere,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)轻松找到我的功能的补充集。
注意: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"),"}",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进行分析;有一个像这样的功能分析独立代码是很好的。使用环境(),将@Spacedman和@Tommy用于改善对foodweb的呼叫的洞察。
(真正的hungaRians会注意到我与名称和类型的顺序不一致 – tooBad :)有一个更长的原因,但这不是正是我使用的代码,反正)
虽然我没有发布由foodweb为我的代码生成的图形的图片,你可以看到一些很好的例子在http://web.archive.org/web/20120413190726/http://www.sigmafield.org/2010/09/21/r-function-of-the-day-foodweb.在我的情况下,它的输出绝对捕获data.table的用法:=和J,以及标准的命名函数,如key和as.data.table。它似乎可以消除我的文本搜索,并且是几种方式的改进(例如查找我忽略的功能)。
总而言之,foodweb是一个很好的工具,我鼓励别人探索mvbutils包和Mark Bravington的其他一些不错的包,比如调试。如果你安装mvbutils,只需检查一下?changed.funs,如果你认为只有你在管理不断发展的R代码的时候才能努力。