From gavin.simpson at ucl.ac.uk  Sun Aug  9 22:18:12 2009
From: gavin.simpson at ucl.ac.uk (Gavin Simpson)
Date: Sun, 09 Aug 2009 21:18:12 +0100
Subject: [Rd] Problem using model.frame with argument subset in own
 function
In-Reply-To: <40e66e0b0908090932i615dea16tf64c8015d30d05b3@mail.gmail.com>
References: <1249756293.2242.15.camel@graptoleberis.geog.ucl.ac.uk>
	<40e66e0b0908090932i615dea16tf64c8015d30d05b3@mail.gmail.com>
Message-ID: <1249849092.2371.28.camel@graptoleberis.geog.ucl.ac.uk>

On Sun, 2009-08-09 at 11:32 -0500, Douglas Bates wrote:
> On Sat, Aug 8, 2009 at 1:31 PM, Gavin Simpson<gavin.simpson at ucl.ac.uk> wrote:
> > Dear List,
> 
> > I am writing a formula method for a function in a package I maintain. I
> > want the method to return a data.frame that potentially only contains
> > some of the variables in 'data', as specified by the formula.
> 
> The usual way to call model.frame (the method that Thomas Lumley has
> called "the standard, non-standard evaluation) is to match the call to
> foo, replace the name of the function being called with
> as.name("model.frame") and force an evaluation in the parent frame.
> it looks like
> 

Thanks Doug. I also received an off-list reply from Brian Ripley
suggesting two alternative approaches.

The bit I was missing was how to manipulate other aspects of the call -
it hadn't clicked that the arguments of the function can be manipulated
by altering the components of the matched call.

In the end I came up with something like:

    mf <- match.call()
    mf[[1]] <- as.name("model.frame")
    mt <- terms(formula, data = data, simplify = TRUE)
    mf[[2]] <- formula(mt, data = data)
    mf$na.action <- substitute(na.action)
    dots <- list(...)
    mf[[names(dots)]] <- NULL
    mf <- eval(mf,parent.frame())
    tran.default(mf, ...)

which seems to be working in the tests I have been running, allowing me
to pass along some components of the call to model.frame, whilst
reserving ... for the default methods arguments, and also get the
simplified formula.

All the best,

G

>     mf <- match.call()
>     if (missing(data)) data <- environment(formula)
>     ## evaluate and install the model frame
>     m <- match(c("formula", "data", "subset", "weights", "na.action", "offset"),
>                names(mf), 0)
>     mf <- mf[c(1, m)]
>     mf$drop.unused.levels <- TRUE
>     mf[[1]] <- as.name("model.frame")
>     fr <- eval(mf, parent.frame())
> 
> The point of all of this manipulation is to achieve the kind of result
> you need where the subset argument is evaluated in the correct
> environmnent.
> 
> > The problem I am having is in writing the function and wrapping it
> > around model.frame. Consider the following data frame:
> >
> > dat <- data.frame(A = runif(10), B = runif(10), C = runif(10))
> >
> > And the wrapper function:
> >
> > foo <- function(formula, data = NULL, ..., subset = NULL,
> >                na.action = na.pass) {
> >    mt <- terms(formula, data = data, simplify = TRUE)
> >    mf <- model.frame(formula(mt), data = data, subset = subset,
> >                      na.action = na.action)
> >    ## real function would do more stuff here and pass mf on to
> >    ## other functions
> >    mf
> > }
> >
> > This is how I envisage the function being called. The real world use
> > would have a data.frame with tens or hundreds of components where only a
> > few need to be excluded. Hence wanting formulas of the form below to
> > work.
> >
> > foo(~ . - B, data = dat)
> >
> > The aim is to return only columns A and C in an object returned by
> > model.frame. However, when I run the above, I get the following error:
> >
> >> foo(~ A + B, data = dat)
> > Error in xj[i] : invalid subscript type 'closure'
> >
> > I've tracked this down to the line in model.frame.default
> >
> >    subset <- eval(substitute(subset), data, env)
> >
> > After evaluating this line, subset contains:
> >
> > Browse[1]> subset
> > function (x, ...)
> > UseMethod("subset")
> > <environment: namespace:base>
> >
> > Not NULL, and hence the error later on when calling the internal
> > model.frame code.
> >
> > So the question is, what am I doing wrong?
> >
> > If I leave the subset argument out of the definition of foo and rely
> > upon the default in model.frame.default, the function works as
> > expected.
> >
> > Perhaps the question should be, how do I modify foo() to allow it to
> > have a formal subset argument, passed to model.frame?
> >
> > Any other suggestions gratefully accepted.
> >
> > Thanks in advance,
> >
> > G
> > --
> > %~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%
> >  Dr. Gavin Simpson             [t] +44 (0)20 7679 0522
> >  ECRC, UCL Geography,          [f] +44 (0)20 7679 0565
> >  Pearson Building,             [e] gavin.simpsonATNOSPAMucl.ac.uk
> >  Gower Street, London          [w] http://www.ucl.ac.uk/~ucfagls/
> >  UK. WC1E 6BT.                 [w] http://www.freshwaters.org.uk
> > %~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%
> >
> > ______________________________________________
> > R-devel at r-project.org mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-devel
> >
-- 
%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%
 Dr. Gavin Simpson             [t] +44 (0)20 7679 0522
 ECRC, UCL Geography,          [f] +44 (0)20 7679 0565
 Pearson Building,             [e] gavin.simpsonATNOSPAMucl.ac.uk
 Gower Street, London          [w] http://www.ucl.ac.uk/~ucfagls/
 UK. WC1E 6BT.                 [w] http://www.freshwaters.org.uk
%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%~%



[Rd] Problem using model.frame with argument subset in own function
Gavin Simpson