Skip to content

passing an extra argument to an S3 generic

8 messages · Henrik Bengtsson, ilai, Michael Friendly

#
I'm trying to write some functions extending influence measures to 
multivariate linear models and also
allow subsets of size m>=1 to be considered for deletion diagnostics.  
I'd like these to work roughly parallel
to those functions for the univariate lm where only single case deletion 
(m=1) diagnostics are considered.

Corresponding to stats::hatvalues.lm, the S3 method for class "lm" objects,

 > hatvalues <-function (model, ...)
UseMethod("hatvalues")

 > hatvalues.lm <-
function (model, infl = lm.influence(model, do.coef = FALSE),    ...)
{
     hat <- infl$hat
     names(hat) <- names(infl$wt.res)
     hat
}

I have, for class "mlm" objects

hatvalues.mlm <- function(model, m=1, infl=mlm.influence(model, m=m, 
do.coef = FALSE), ...)
{
     hat <- infl$H
     m <- infl$m
     names(hat) <- if(m==1) infl$subsets else apply(infl$subsets,1, 
paste, collapse=',')
     hat
}

where mlm.influence() does the calculations, but also allows the m= 
argument to specify subset size.
Yet when I test this I can't seem to pass the m= argument directly, so 
that it gets stuffed in to the infl=
call to mlm.influence.

# fit an mlm
library(heplots)
Rohwer2 <- subset(Rohwer, subset=group==2)
rownames(Rohwer2)<- 1:nrow(Rohwer2)
Rohwer.mod <- lm(cbind(SAT, PPVT, Raven) ~ n+s+ns+na+ss, data=Rohwer2)

 > class(Rohwer.mod)
[1] "mlm" "lm"


## this doesn't work, as I would like it to, calling the hatvalues.mlm 
method, but passing m=2:
 > hatvalues(Rohwer.mod, m=2)
Error in UseMethod("hatvalues") :
   no applicable method for 'hatvalues' applied to an object of class 
"c('double', 'numeric')"

I don't understand why this doesn't just call hatvalues.mlm, since 
Rohwer.mod is of class "mlm".

# These work -- calling hatvalues.mlm explicitly, or passing the infl= 
argument with the call to
# mlm.influence
 > hatvalues.mlm(Rohwer.mod, m=2)
 > hatvalues(Rohwer.mod, infl=mlm.influence(Rohwer.mod,m=2))

Can someone help me understand what is wrong and how to make the .mlm 
method allow m= to be passed
directly to the infl= computation?

thx,
-Michael
#
You do not provide mlm.influence() so your code can't be reproduced.

Or did you mean to put lm.influence() in the formals to your hatvalues.mlm ?

If yes, then 1) you have a typo 2) lm.influence doesn't allow you to
pass on arguments, maybe try influence.lm instead.

Elai
On Thu, Feb 9, 2012 at 1:42 PM, Michael Friendly <friendly at yorku.ca> wrote:
#
On 2/9/2012 6:24 PM, ilai wrote:
No, I've written an S3 method, influence.mlm and a computational method,
mlm.influence, both of which take an m= argument.  I didn't post all the 
code because I thought the question might have an easy answer based on 
what I provided.

I include all the code and a test case in the attached .txt file that
can be sourced.

-Michael

  
    
#
For these type of setups, I typically turn to "default" values, e.g.

hatvalues.mlm <- function(model, m=1, infl=NULL, ...)
{
   if (is.null(infl)) {
     infl <- mlm.influence(model, m=m, do.coef=FALSE);
   }

   hat <- infl$H
   m <- infl$m
   names(hat) <- if(m==1) infl$subsets else apply(infl$subsets,1,
paste, collapse=',')
   hat
}

So people may prefer to do the following:

hatvalues.mlm <- function(model, m=1, infl, ...)
{
   if (missing(infl)) {
     infl <- mlm.influence(model, m=m, do.coef=FALSE);
   }

   hat <- infl$H
   m <- infl$m
   names(hat) <- if(m==1) infl$subsets else apply(infl$subsets,1,
paste, collapse=',')
   hat
}

The downside with this approach is that args(<fcn>) doesn't reveal the
default behavior; instead you need to document it clearly in the
help(<fcn>).

My $.02

/H
On Fri, Feb 10, 2012 at 12:13 PM, Michael Friendly <friendly at yorku.ca> wrote:
#
On 2/10/2012 4:09 PM, Henrik Bengtsson wrote:
Thanks;  I tried exactly that, but I still can't pass m=2 to the mlm 
method through the generic

 > hatvalues(Rohwer.mod)
          1          2          3          4          5          
6          7          8
0.16700926 0.21845327 0.14173469 0.07314341 0.56821462 0.15432157 
0.04530969 0.17661104
          9         10         11         12         13         
14         15         16
0.05131298 0.45161152 0.14542776 0.17050399 0.10374592 0.12649927 
0.33246744 0.33183461
         17         18         19         20         21         
22         23         24
0.17320579 0.26353864 0.29835817 0.07880597 0.14023750 0.19380286 
0.04455330 0.20641708
         25         26         27         28         29         
30         31         32
0.15712604 0.15333879 0.36726467 0.11189754 0.30426999 0.08655434 
0.08921878 0.07320950
 > hatvalues(Rohwer.mod, m=2)
Error in UseMethod("hatvalues") :
   no applicable method for 'hatvalues' applied to an object of class 
"c('double', 'numeric')"

## This works:
 > hatvalues.mlm(Rohwer.mod, m=2)
    ... output snipped

 > hatvalues
function (model, ...)
UseMethod("hatvalues")
<bytecode: 0x021339e4>
<environment: namespace:stats>
 >

-Michael
#
You are setting a new class ("inflmlm") at the end of mlm.influence.
Remove that second to last line and enjoy your new S3 method.

I'm not sure, but I think it is just the new class "inflmlm" applied
to inf in the formals of hatvalues.mlm confused the dispatch
mechanism. You would think the error message will call the offending
class not "numeric" double" but that's above my pay grade...

You could probably put back the inflmlm class assignment with an
explicit call to UseMethod in hatvalues.mlm ?

Cheers
On Fri, Feb 10, 2012 at 2:35 PM, Michael Friendly <friendly at yorku.ca> wrote:
2 days later
#
On 2/11/2012 12:00 PM, ilai wrote:
Thanks for the suggestion, but it doesn't help -- I still get the same
behavior whether mlm.influence returns a classed object or not.
As well, I am defining print.inflmlm() and as.data.frame.inflmlm() 
methods for these objects, so I do need mlm.influence to return a 
classed object.

My hatvalues.mlm is designed to be similar in structure to 
stats::hatvalues.lm where the S3 generic is defined.

hatvalues.mlm <- function(model, m=1, infl, ...)
{
    if (missing(infl)) {
      infl <- mlm.influence(model, m=m, do.coef=FALSE);
    }
     hat <- infl$H
     m <- infl$m
     names(hat) <- if(m==1) infl$subsets else apply(infl$subsets,1, 
paste, collapse=',')
     hat
}

 > hatvalues
function (model, ...)
UseMethod("hatvalues")
<bytecode: 0x0326fd30>
<environment: namespace:stats>
 > hatvalues.lm
function (model, infl = lm.influence(model, do.coef = FALSE),
     ...)
{
     hat <- infl$hat
     names(hat) <- names(infl$wt.res)
     hat
}
<bytecode: 0x0326de6c>
<environment: namespace:stats>

The idea is that the infl= argument specifies a call to the 
computational function, mlm.influence() in my case, just as 
lm.influence() does in the stats package.

The logic of UseMethod is that it should dispatch on the class of the 
*first* argument to the function, which in my test case is c("mlm", "lm")

 > Rohwer.mod <- lm(cbind(SAT, PPVT, Raven) ~ n+s+ns+na+ss, data=Rohwer2)
 > class(Rohwer.mod)
[1] "mlm" "lm"

 > trace(hatvalues)
 > hatvalues(Rohwer.mod, m=2)
trace: hatvalues(Rohwer.mod, m = 2)
Error in UseMethod("hatvalues") :
   no applicable method for 'hatvalues' applied to an object of class 
"c('double', 'numeric')"
 > hatvalues(Rohwer.mod)
trace: hatvalues(Rohwer.mod)
          1          2          3          4          5          6 
     7          8
0.16700926 0.21845327 0.14173469 0.07314341 0.56821462 0.15432157 
0.04530969 0.17661104
...


I'm still stumped on why with the extra argument m=2, R sees this
as an object of class c('double', 'numeric').  As well, I can't see
any way to debug this.

  
    
#
Hi Michael,
Try the attached. The only change to your script is in the first line
where I explicitly tell hatvalues to use methods (the "infmlm" class
stays). I also commented out all your TESTME at the end.

source('mlminfl-testHELP.R')

Now this should have worked for you too. Let me know. Sorry about that
"just remove the class". Had somewhat of a brain glitch when writing
the E-mail and wasn't clear.

Cheers
On Tue, Feb 14, 2012 at 8:05 AM, Michael Friendly <friendly at yorku.ca> wrote: