如何列出特定包/命名空间中为特定通用函数定义的所有S3方法

李哲源*_*李哲源 6 methods r function

我知道我可以列出所有S3方法为特定的通用功能,比如说summary,由

.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*  
Run Code Online (Sandbox Code Playgroud)

但是,我知道其中一些功能来自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.
Run Code Online (Sandbox Code Playgroud)

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

.S3methods("summary", envir = getNamespace("base"))
Run Code Online (Sandbox Code Playgroud)

在手册的"详细信息"部分,有人说:

'methods()'找到与'generic.function'或'class'参数相关的S3和S4方法.在当前"search()"路径的所有包中都可以找到方法.'.S3methods()'只找到S3方法,'.S4methods()'只找到S4方法.

所以基本上它否认了论证的使用envir.

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

李哲源*_*李哲源 6

MrFlick的回答(现在很遗憾被删除)非常有帮助.忘记检查返回的值是我的错.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"
Run Code Online (Sandbox Code Playgroud)

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

xx[yy$from == "stats"]
#[1] "summary.aov"     "summary.glm"     "summary.lm"      "summary.manova" 
#[5] "summary.stepfun"
Run Code Online (Sandbox Code Playgroud)

在我发布这个问题之后,我意识到通过正则表达式有一种方法.这表明实际上有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"
Run Code Online (Sandbox Code Playgroud)

所以在找到任何替代解决方案之前,我会继续使用正则表达式.这是一个功能.

## 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")
Run Code Online (Sandbox Code Playgroud)

咳,正则表达式是越野车!

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"
Run Code Online (Sandbox Code Playgroud)

seq.int不是"int"方法seq.无论是sort.int也不sort.list是"INT"或"名单"的方法sort.它们只是.功能名称中的独立功能.

.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>
Run Code Online (Sandbox Code Playgroud)

所以最安全的方法可能仍然是使用返回的值.S3methods.

使用getAnywhere(getS3method)

回到"摘要"的例子.不是从命名空间,即那些有导出的函数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
Run Code Online (Sandbox Code Playgroud)

但是,既然我们知道这些功能的名称,为什么不适用getAnywhere它们呢?

zz <- lapply(xx[!yy$visible], getAnywhere)
Run Code Online (Sandbox Code Playgroud)

然后可以通过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"
Run Code Online (Sandbox Code Playgroud)

最终解决方案

现在让我将这些想法结合到一个函数中.

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

最后,我仍然使用正则表达式来捕获.其名称中的函数,但这些函数不是正确的方法.除了"可见"和"隐形"之外,它们被归类为"misc".

测试

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)
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

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