如何在[R]中确定命名空间导入顺序

时间:2012-06-27 17:41:48

标签: r ggplot2

我需要清理一个R实例,将其恢复到启动时的处女状态。到目前为止,我正在做的是:

启动时,记录加载的包和名称空间

original_packages <- grep('^package:', search(), value = TRUE)
original_namespaces <- loadedNamespaces()

当我需要刷新实例时,请分离启动时不存在的每个已加载包:

for (pkg in grep('^package:', search(), value = TRUE)) {
    if (! pkg %in% original_packages){
        detach(pkg, unload=TRUE, force=TRUE, character.only=TRUE)
    }
}

问题是,如果我已经加载了一堆带有一些导入的命名空间的包,例如ggplot2,那些命名空间会保持加载状态,我必须按照导入的顺序从高级别下载中卸载它们。只是盲目地卸载它们不起作用,因为我得到“命名空间'x'由'y'导入,'z'因此无法卸载”错误。

这是可重复的例子:

original_packages <- grep('^package:', search(), value = TRUE)
original_namespaces <- loadedNamespaces()

library(ggplot2)
library(plyr)

loadedNamespaces()

for (pkg in grep('^package:', search(), value = TRUE)) {
    if (! pkg %in% original_packages){
        detach(pkg, unload=TRUE, force=TRUE, character.only=TRUE)
    }
}

for (ns in loadedNamespaces()) {
    if (! ns %in% original_namespaces){
        unloadNamespace(ns)
    }
}

有没有办法找出命名空间导入层次结构?如果是这样,那么我应该能够正确地订购最后一个循环......

2 个答案:

答案 0 :(得分:0)

好吧,我已经把一个hacky解决方案归结为一个公认的hacky需求。任何有关如何做得更好的建议将不胜感激。特别是,我对全局<<-对象的namespace_depths分配不满意。

original_packages <- grep('^package:', search(), value = TRUE)
original_namespaces <- loadedNamespaces()

library(ggplot2)
library(plyr)

loadedNamespaces()

new_packages <- Filter(function(pkg) { ! pkg %in% original_packages }, grep('^package:', search(), value = TRUE))

new_namespaces <- Filter(function(ns) { ! ns %in% original_namespaces }, loadedNamespaces())

get_imports <- function(ns, depth) {

    imports <- Filter(function(ns) { ! ns %in% original_namespaces }, names(getNamespaceInfo(ns, 'imports')))
    if (length(imports) == 0 ) {
        if ( is.null(namespace_depths[[ns]]) || namespace_depths[[ns]] < depth){
            namespace_depths[[ns]] <<- depth
        }
        return()
    }
    for (imported_ns in imports){
        get_imports(imported_ns, depth + 1)
    }
    if ( is.null(namespace_depths[[ns]]) || namespace_depths[[ns]] < depth){
        namespace_depths[[ns]] <<- depth
    }
}

namespace_depths <- list()
sapply(new_namespaces, get_imports, 0)

for (ns in names(namespace_depths)[order(unlist(namespace_depths))]) {
    if (! ns %in% original_namespaces){
        unloadNamespace(ns)
    }
}

for (pkg in new_packages){
    detach(pkg, unload=TRUE, force=TRUE, character.only=TRUE)
}

答案 1 :(得分:0)

正如@Josh O'Brien提到的那样,通过分离或卸载名称空间来获取干净的环境是不可能的。

但是,在这里回答您的问题是使用tools:::dependsOnPkgs按正确顺序卸载所有命名空间的简单方法:

## params: originalNamespaces is a list of namespaces you want to keep
cleanNamespaces <- function(originalNamespaces) {

    ## which namespaces should be removed?
    ns <- setdiff(loadedNamespaces(), originalNamespaces)

    ## get dependency list
    dep <- unlist(lapply(ns, tools:::dependsOnPkgs))

    ## append namespaces to guarantee to fetch namespaces with 
    ## no reverse dependencies
    ns <- c(dep, ns)

    ## get namespace names in correct order to unload without errors
    ns <- names(sort(table(ns), decreasing=TRUE))

    ## only unload namespaces which are attached
    ns <- ns[ns %in% loadedNamespaces()]

    ## unload namespaces
    invisible(sapply(ns, unloadNamespace))
}