如何扫描一个包中的所有函数形式来寻找一个特定的参数,比如'na.rm'?

e0bqpujr  于 2023-03-15  发布在  其他
关注(0)|答案(1)|浏览(116)

我想先回顾一下na.rm在基本包中的使用,但一般的想法是在任何包中找到任何参数。

Package

packages = (.packages());

包中的函数

我不知道这有多可靠,因为它后来在formals上抛出错误

# https://stackoverflow.com/questions/8696158/find-all-functions-including-private-in-a-package
functions.listFromPackage = function(pkg = "stats")
    {
    all     = ls( getNamespace(pkg),        all.names = TRUE); 
    # public has to be loaded ... 
    public  = ls( paste0("package:", pkg),  all.names = TRUE);
    private = setdiff(all, public);
    list("public" = public, "private" = private);
    }

正式

由于上面的列表提供了字符串,我想我必须进行解析,以便形式化程序喜欢输入。

has.na = NULL;
## LOOP over fn.str in package
fn.obj = eval(parse(text=fn.str));
x = formals( fn.obj );
if(is.primitive(fn.obj)) { x = formals(args(fn.obj)); }
if("na.rm" %in% names(x) )
          {
            row = c(package, fn, x$na.rm);
            has.na = rbind(has.na, row);
          }

初步结果

> has.na
    [,1]      [,2]              [,3]   
row "stats"   "approx"          "TRUE" 
row "stats"   "approxfun"       "TRUE" 
row "stats"   "density.default" "FALSE"
row "stats"   "fivenum"         "TRUE" 
row "stats"   "heatmap"         "TRUE" 
row "stats"   "IQR"             "FALSE"
row "stats"   "mad"             "FALSE"
row "stats"   "median"          "FALSE"
row "stats"   "median.default"  "FALSE"
row "stats"   "medpolish"       "FALSE"
row "stats"   "sd"              "FALSE"
row "stats"   "var"             "FALSE"
row "methods" "Summary"         "FALSE"
row "base"    ".colMeans"       "FALSE"
row "base"    ".colSums"        "FALSE"
row "base"    ".rowMeans"       "FALSE"
row "base"    ".rowSums"        "FALSE"
row "base"    "all"             "FALSE"
row "base"    "any"             "FALSE"

它抛出了很多错误。

Warning messages:
1: In formals(fun) : argument is not a function

# for example fn.str = "p.adjust.methods"
Error in get(fun, mode = "function", envir = envir) : 
  first argument has length > 1

# for example fn.str = "contrasts<-"
Error in parse(text = fn) : <text>:2:0: unexpected end of input
1: contrasts<-
# obvious why `eval` fails here.  How to get the formals in a variadic way (as a list of strings?)

所以我认为我的函数集合可能不准确?

问题:如何编写一个函数来捕获所有使用给定包集合的参数(默认值)的函数?

==可接受溶液==

match.fun()是该解决方案的关键元素,我让函数在GLOBAL ls(all.names=TRUE)上循环以获取本地函数,然后在package调用中指定的包列表上循环。
下面是一个结果的head/str:

> head(df)
        pkg  fn.is         fn.name param.value
row   stats public          approx        TRUE
row.1 stats public       approxfun        TRUE
row.2 stats public density.default       FALSE
row.3 stats public         fivenum        TRUE
row.4 stats public         heatmap        TRUE
row.5 stats public             IQR       FALSE
> str(df)
'data.frame':   44 obs. of  4 variables:
 $ pkg        : chr  "stats" "stats" "stats" "stats" ...
 $ fn.is      : chr  "public" "public" "public" "public" ...
 $ fn.name    : chr  "approx" "approxfun" "density.default" "fivenum" ...
 $ param.value: chr  "TRUE" "TRUE" "FALSE" "TRUE" ...
 - attr(*, "param")= chr "na.rm"
 - attr(*, "packages")= chr [1:2] "stats" "base"
>

我故意不强制转换为 Dataframe 上的任何内容。na.rm示例具有独特的:TRUE、FALSE、“”(作为函数参数列出,未分配任何默认值)。函数的后期处理可能会创建factors等。
TODO:允许参数为向量,例如使用jsonlite::FUNCTIONS的JSON.stringify

3qpi33ja

3qpi33ja1#

程序包中的函数

x <- ls(getNamespace('stats'), all.names = TRUE)
y <- sapply(x, function(x) inherits(try(match.fun(x), silent = TRUE), 'try-error'))
f <- x[!y]
head(f)
# [1] ".checkMFClasses" ".getXlevels"     ".lm.fit"         ".MFclass"        ".nknots.smspl"   ".preformat.ts"

na.rm作为参数的函数

z <- sapply(f, function(x) is.null(formals(match.fun(x))$na.rm))
head(f[!z])
# [1] "approx"          "approxfun"       "density.default" "fivenum"         "heatmap"         "IQR"

每个的默认值na.rm

head(sapply(f[!z], function(x) formals(match.fun(x))$na.rm))
# approx       approxfun density.default         fivenum         heatmap             IQR 
#   TRUE            TRUE           FALSE            TRUE            TRUE           FALSE

您可以将这些步骤合并为一个步骤

f <- function(package) {
  p <- ls(getNamespace(package), all.names = TRUE)
  y <- lapply(p, function(x) {
    mf <- try(match.fun(x), silent = TRUE)
    pr <- is.primitive(mf)
    
    if (inherits(mf, 'try-error') ||
        is.null(args(mf)) ||
        (pr && !is.logical(formals(args(mf))$na.rm)) ||
        (!is.logical(formals(mf)$na.rm)))
      NULL
    else {
      data.frame(
        package = package,
        'function' = x,
        na.rm = if (pr)
          formals(args(mf))$na.rm else formals(mf)$na.rm
      )
    }
  })
  
  do.call('rbind', y)
}

f('stats')
#    package       function. na.rm
# 1    stats          approx  TRUE
# 2    stats       approxfun  TRUE
# 3    stats density.default FALSE
# 4    stats         fivenum  TRUE
# 5    stats         heatmap  TRUE
# 6    stats             IQR FALSE
# 7    stats             mad FALSE
# 8    stats          median FALSE
# 9    stats  median.default FALSE
# 10   stats       medpolish FALSE
# 11   stats              sd FALSE
# 12   stats             var FALSE

f('methods')
#   package function. na.rm
# 1 methods   Summary FALSE

f('base')
#    package         function. na.rm
# 1     base         .colMeans FALSE
# 2     base          .colSums FALSE
# 3     base         .rowMeans FALSE
# 4     base          .rowSums FALSE
# 5     base          colMeans FALSE
# 6     base           colSums FALSE
# 7     base       is.unsorted FALSE
# 8     base      mean.default FALSE
# 9     base              pmax FALSE
# 10    base          pmax.int FALSE
# 11    base              pmin FALSE
# 12    base          pmin.int FALSE
# 13    base     range.default FALSE
# 14    base          rowMeans FALSE
# 15    base rowsum.data.frame FALSE
# 16    base    rowsum.default FALSE
# 17    base           rowSums FALSE

相关问题