如何列出在特定程序包/名称空间中为特定泛型功能定义的所有S3方法

时间:2018-08-07 14:26:44

标签: r function methods

我知道我可以列出某特定通用函数(例如summary的所有S3方法,

.S3methods("summary")
# [1] summary.aov                    summary.aovlist*              
# [3] summary.aspell*                summary.check_packages_in_dir*
# [5] summary.connection             summary.data.frame            
# [7] summary.Date                   summary.default               
# [9] summary.ecdf*                  summary.factor                
#[11] summary.glm                    summary.infl*                 
#[13] summary.lm                     summary.loess*                
#[15] summary.manova                 summary.matrix                
#[17] summary.mlm*                   summary.nls*                  
#[19] summary.packageStatus*         summary.PDF_Dictionary*       
#[21] summary.PDF_Stream*            summary.POSIXct               
#[23] summary.POSIXlt                summary.ppr*                  
#[25] summary.prcomp*                summary.princomp*             
#[27] summary.proc_time              summary.srcfile               
#[29] summary.srcref                 summary.stepfun               
#[31] summary.stl*                   summary.table                 
#[33] summary.tukeysmooth*  

但是,我知道其中一些功能来自stats,而某些功能来自base。如果我将更多软件包加载到R中,则此列表可能会更长。因此,我想将搜索限制在特定的程序包/名称空间中,但我找不到实现此目的的方法。

?.S3methods的手册似乎是自相矛盾的。

 .S3methods(generic.function, class, envir=parent.frame())

envir: the environment in which to look for the definition of the
       generic function, when the generic function is passed as a
       character string.

所以我尝试了以下操作,但仍然显示了所有方法:

.S3methods("summary", envir = getNamespace("base"))

在手册的“详细信息”部分中说:

  

“ methods()”查找与“ generic.function”或“ class”自变量关联的S3和S4方法。 在当前“ search()”路径上的所有程序包中都找到方法。“ .. S3methods()”仅找到S3方法,“。S4methods()”仅找到S4方法。

因此,基本上它拒绝使用参数envir

无论如何,我可以实现受限的搜索和显示。

1 个答案:

答案 0 :(得分:6)

MrFlick's answer(现在不幸地删除了)非常有帮助。忘记检查.S3methods的返回值是我的错。但是,他的回答并不能完全解决问题。

xx <- .S3methods("summary")
yy <- attr(xx, "info")
levels(yy$from)
#[1] "base"                            "datasets"                       
#[3] ".GlobalEnv"                      "graphics"                       
#[5] "grDevices"                       "methods"                        
#[7] "stats"                           "utils"                          
#[9] "registered S3method for summary"

级别“已注册的S3摘要方法”相当模糊。使用这种方法,只能显示stats包中的5个结果:

xx[yy$from == "stats"]
#[1] "summary.aov"     "summary.glm"     "summary.lm"      "summary.manova" 
#[5] "summary.stepfun"

发布此问题后,我才意识到有一种通过 regex 的方法。这表明实际上有16个匹配。

grep("^summary.", ls(getNamespace("stats")), value = TRUE)
# [1] "summary.aov"         "summary.aovlist"     "summary.ecdf"       
# [4] "summary.glm"         "summary.infl"        "summary.lm"         
# [7] "summary.loess"       "summary.manova"      "summary.mlm"        
#[10] "summary.nls"         "summary.ppr"         "summary.prcomp"     
#[13] "summary.princomp"    "summary.stepfun"     "summary.stl"        
#[16] "summary.tukeysmooth"

因此,在找到任何替代解决方案之前,我将继续使用 regex 。这是一个功能。

## provide (generic) function name and package name as strings
findS3Fun <- function (Fun, pkg) {
  all_fun <- ls(getNamespace(pkg))
  all_fun[startsWith(all_fun, sprintf("%s.", Fun))]
  }

findS3Fun("summary", "stats")

哎呀, regex 是越野车!

findS3Fun实际上是越野车。

findS3Fun("seq", "base")
#[1] "seq.Date"    "seq.default" "seq.int"     "seq.POSIXt" 

findS3Fun("sort", "base")
#[1] "sort.default" "sort.int"     "sort.list"    "sort.POSIXlt"

seq.int不是seq的“ int”方法。 sort.intsort.list都不是sort的“ int”或“ list”方法。它们只是函数名称中带有.的独立函数。

.S3methods("seq")
#[1] seq.Date    seq.default seq.POSIXt 

.S3methods("sort")
#[1] sort.bibentry* sort.default   sort.POSIXlt 

## this function is from package `utils` not `base`
environment(getS3method("sort", "bibentry"))
#<environment: namespace:utils>

因此,最安全的方法可能仍是使用返回的.S3methods值。

使用getAnywheregetS3method

使用“摘要”返回示例。未从名称空间导出的函数(即具有yy$visible = FALSE的函数具有yy$from = "registered S3method for summary"

with(yy, from[!visible])
# [1] registered S3method for summary registered S3method for summary
# [3] registered S3method for summary registered S3method for summary
# [5] registered S3method for summary registered S3method for summary
# [7] registered S3method for summary registered S3method for summary
# [9] registered S3method for summary registered S3method for summary
#[11] registered S3method for summary registered S3method for summary
#[13] registered S3method for summary registered S3method for summary
#[15] registered S3method for summary registered S3method for summary
#8 Levels: base datasets graphics grDevices methods stats ... registered S3method for summary

但是,由于我们知道这些函数的名称,为什么不对它们应用getAnywhere

zz <- lapply(xx[!yy$visible], getAnywhere)

然后可以通过一些努力从zz中提取包/名称空间信息。但是,此lapply + getAnywhere相当慢。由于getAnywhere返回的东西超出了我的需要,因此我深入研究了它的源代码以查看是否可以进行一些修整。事实证明我可以。

hidden <- xx[!yy$visible]
# [1] "summary.aovlist"               "summary.aspell"               
# [3] "summary.check_packages_in_dir" "summary.ecdf"                 
# [5] "summary.infl"                  "summary.loess"                
# [7] "summary.mlm"                   "summary.nls"                  
# [9] "summary.packageStatus"         "summary.PDF_Dictionary"       
#[11] "summary.PDF_Stream"            "summary.ppr"                  
#[13] "summary.prcomp"                "summary.princomp"             
#[15] "summary.stl"                   "summary.tukeysmooth"  

CLASS <- substr(hidden, nchar("summary") + 2L, nchar(hidden))
#[1] "aovlist"               "aspell"                "check_packages_in_dir"
# [4] "ecdf"                  "infl"                  "loess"                
# [7] "mlm"                   "nls"                   "packageStatus"        
#[10] "PDF_Dictionary"        "PDF_Stream"            "ppr"                  
#[13] "prcomp"                "princomp"              "stl"                  
#[16] "tukeysmooth"          

vapply(CLASS,
       function (u) getNamespaceName(environment(getS3method("summary", u)))[[1L]],
       "", USE.NAMES = FALSE)
# [1] "stats" "utils" "tools" "stats" "stats" "stats" "stats" "stats" "utils"
#[10] "tools" "tools" "stats" "stats" "stats" "stats" "stats"

最终解决方案

现在让我将这些想法总结成一个函数。

findS3Fun <- function (Fun, pkg) {
  xx <- .S3methods(Fun)
  yy <- attr(xx, "info")[1:2]
  where <- yy[[2L]]  ## yy$from
  where <- levels(where)[where]  ## factor to character
  hidden <- !yy[[1L]]  ## !yy$visible
  hidden_xx <- xx[hidden]  ## hidden functions
  if (length(hidden) > 0L) {
    CLASS <- substr(hidden_xx, nchar(Fun) + 2L, nchar(hidden_xx))
    aux <- function (u) getNamespaceName(environment(getS3method(Fun, u)))[[1L]]
    where[hidden] <- vapply(CLASS, aux, "", USE.NAMES = FALSE)
    }
  export <- where == pkg
  xx <- xx[export]
  visible <- yy[[1L]][export]
  ## use "regex" to find functions with "." in their names but not methods
  all_fun <- ls(getNamespace(pkg))
  all_fun <- all_fun[startsWith(all_fun, sprintf("%s.", Fun))]
  misc <- all_fun[!(all_fun %in% xx)]
  ## return functions by category
  list(visible = xx[visible], invisible = xx[!visible], misc = misc)
  }

最后,我仍然使用 regex 来捕获名称中带有.的函数,但这不是正确的方法。除了“可见”和“不可见”之外,它们还被分类为“杂项”。

测试

findS3Fun("summary", "stats")
#$visible
#[1] "summary.aov"     "summary.glm"     "summary.lm"      "summary.manova" 
#[5] "summary.stepfun"
#
#$invisible
# [1] "summary.aovlist"     "summary.ecdf"        "summary.infl"       
# [4] "summary.loess"       "summary.mlm"         "summary.nls"        
# [7] "summary.ppr"         "summary.prcomp"      "summary.princomp"   
#[10] "summary.stl"         "summary.tukeysmooth"
#
#$misc
#character(0)

findS3Fun("sort", "base")
#$visible
#[1] "sort.default" "sort.POSIXlt"
#
#$invisible
#character(0)
#
#$misc
#[1] "sort.int"  "sort.list"

findS3Fun("[", "base")
#$visible
# [1] "[.AsIs"            "[.data.frame"      "[.Date"           
# [4] "[.difftime"        "[.Dlist"           "[.factor"         
# [7] "[.hexmode"         "[.listof"          "[.noquote"        
#[10] "[.numeric_version" "[.octmode"         "[.POSIXct"        
#[13] "[.POSIXlt"         "[.simple.list"     "[.table"          
#[16] "[.warnings"       
#
#$invisible
#character(0)
#
#$misc
#character(0)

findS3Fun("[[", "base")
#$visible
#[1] "[[.data.frame"      "[[.Date"            "[[.factor"         
#[4] "[[.numeric_version" "[[.POSIXct"        
#
#$invisible
#character(0)
#
#$misc
#character(0)