通用方法'因素'和' as.factor'

时间:2015-01-24 01:40:20

标签: r generics s4

我在R中开发了一个S3类,其行为与因子变量非常相似,但并不完全相同。我在实施中留下的唯一一个问题是factoras.factor不是泛型。

我通过覆盖我的包中的base::factor函数中的.onload覆盖了我个人使用的限制,如下所示:

.onAttach <- function(libname,pkgname){

    # note that as.factor is not a generic -- need to override it
    methods:::bind_activation(on = TRUE)

    # TODO: make a better attmept to deterime if base::factor is a generic or not.
    if(!length(ls(pattern='^as\\.factor\\.default$', envir=as.environment('package:base'),all.names=TRUE))){

        # bind  the current implementation of 'as.factor' to 'as.factor.default'
        assign('as.factor.default',
               base:::as.factor,
               envir=as.environment('package:base'))

        # unock the binding for 'as.factor' 
        unlockBinding('as.factor', as.environment('package:base'))

        # bind the generic to 'as.factor' in the 'package:base'
        assign('as.factor',
               function (x,...) UseMethod('as.factor') ,
               envir=as.environment('package:base'))

        # re-lock the binding for 'as.factor' 
        lockBinding('as.factor', as.environment('package:base'))
    }
    [similar code for making 'factor' and 'table' behave as generics excluded]
}

但是我知道修改base永远不会在CRAN上飞行,所以我很好奇是否有解决方法。正如@BondedDust指出的那样,我当然可以将我的函数重命名为普通因子(目前命名为as.factor.MYCLASS)的强制类似于As.factor,但我宁愿不去那条路,因为这意味着用户必须编写如下代码:

#coerce x to a factor
if(inherits(x,'MYCLASS'))
    x <- As.factor(x)
else
    x <- as.factor(x)

if(inherits(x,'MYCLASS'))
    x <- Factor(x)
else
    x <- factor(x)

将因素强制作为一般因素并未实施,这感觉很奇怪。

我也尝试过.onAttach

的这种实现
.onAttach <- function(libname,pkgname){

    setOldClass(c("MYCLASS"),
                where=as.environment('package:MyPackage'))

    setMethod('factor',
            signature(x='MYCLASS'),
            factor.MYCLASS,
            where=as.environment('package:MyPackage'))

}

但我收到此错误消息:

Error in rematchDefinition(definition, fdef, mnames, fnames, signature) : 
   methods can add arguments to the generic ‘factor’ only if '...' is an 

因为factor不使用dots参数而我的factor.MYCLASS还有一个参数。

2 个答案:

答案 0 :(得分:1)

绝对不需要替换base函数。只需在包中覆盖它们即可使它们成为通用的。

所以,在你的包中,做:

factor = function (...)
    UseMethod('factor')

factor.default = base::factor

factor.MyClass = function (...) your logic

由于您的程序包将在attach之后base进行修改,因此将首先找到此factor重新定义。

答案 1 :(得分:0)

回答了我自己的问题。下面的代码替换了我的包中的原始.onLoad函数。这并不能完全满足我的用户希望能够调用as.factor(obj,arg='arg') obj MYCLASS是一个具有类.onLoad的对象的愿望,所以我把代码放在原来的{{1}上面的方法到一个名为setGenerics()的函数中,该函数根据用户的请求为factoras.factor创建S3泛型。

我对此解决方案非常满意。我只是希望这能满足CRAN的要求。

# create a virtual S4 class from my S3 class
setOldClass(c("MYCLASS"))

# set methods for the virtual S4 classes of 'ordered','factor'
setMethod('as.ordered',
        signature(x='MYCLASS'),
        function(x)as.factor.MYCLASS(x,ordered=T))

setMethod('as.factor',
        signature(x='MYCLASS'),
        function(x)as.factor.MYCLASS(x))

setMethod('factor',
        signature(x='MYCLASS'),
        # re-capitulate the signature for base::factor()
        function (x , levels, labels = levels, exclude = NA, 
            ordered = is.ordered(x), nmax = NA) {
            ARGS <- list(x=x)
            if(!missing(levels))
                args['levels'] <- levels
            if(!missing(labels))
                args['labels'] <- labels
            if(!missing(exclude))
                args['exclude'] <- exclude
            if(!missing(ordered))
                args['ordered'] <- ordered
            if(!missing(nmax))
                warning('unused argument `nmax` in factor.MYCLASS')
            do.call(as.factor.MYCLASS,ARGS)
        })


setGenerics <- function(){

    [contents from the original .onLoad method]

}

.onAttach <- function(libname,pkgname)
    cat('Call setGenerics() for increased compatibility with `factor`, `as.factor`, and `table`.\n')