Reputation: 73265
I know I can list all S3 methods for a particular generic function, say summary
, by
.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*
However, I know that some of these functions come from stats
, while some from base
. If I load more packages into R this list can be much longer. I therefore want to restrict the search in a particular package / namespace, but I don't find a way to do this.
The manual of ?.S3methods
appears to be self-contradicted.
.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.
So I tried the following but still get all methods displayed:
.S3methods("summary", envir = getNamespace("base"))
In "Details" section of the manual, it is said:
‘methods()’ finds S3 and S4 methods associated with either the ’generic.function’ or ‘class’ argument. Methods are found in all packages on the current ‘search()’ path. ‘.S3methods()’ finds only S3 methods, ‘.S4methods()’ finds only only S4 methods.
So basically it denies the use of argument envir
.
Is there anyway I can achieve a restricted search and display.
Upvotes: 7
Views: 2522
Reputation: 73265
MrFlick's answer (now unfortunately deleted) is very helpful. It is my fault to have forgotten to check the returned values of .S3methods
. However, his answer does not completely resolve the matter.
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"
The level "registered S3method for summary" is rather ambiguous. With this approach, only 5 results from stats
package can be displayed:
xx[yy$from == "stats"]
#[1] "summary.aov" "summary.glm" "summary.lm" "summary.manova"
#[5] "summary.stepfun"
Just after my posting this question, I realized that there is one way via regex. And this reveals that in fact there are 16 hits.
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"
So before any alternative solution is found, I would stay with using regex. Here is a function.
## 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")
findS3Fun
is actually buggy.
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
is not the "int" method for seq
. Neither sort.int
nor sort.list
is the "int" or "list" method for sort
. They are just stand alone functions with .
in their function name.
.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>
So the safest approach is probably still to work with the returned values of .S3methods
.
getAnywhere
(getS3method
)Back to the example with "summary". Functions that are not exported from namespaces, i.e., those with yy$visible = FALSE
, have 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
However, since we know the names of these functions, why not apply getAnywhere
on them?
zz <- lapply(xx[!yy$visible], getAnywhere)
Then the package / namespace information can be extracted from zz
with some effort. However, this lapply
+ getAnywhere
is quite slow. Since getAnywhere
returns more things than I needed, I dug into its source code to see whether I could do some trimming. It turns out that I can.
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"
Now let me wrap up these ideas into a function.
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)
}
In the end I still use regex to catch functions with .
in their names but are not rightful methods. They are classified as "misc", besides "visible" and "invisible".
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)
Upvotes: 8