[R] formula wrangling

John Fox j|ox @end|ng |rom mcm@@ter@c@
Mon Sep 21 20:42:18 CEST 2020


Dear Roger,

This is an interesting puzzle and I started to look at it when your 
second message arrived. I can simplify your code slightly in two places, 
here:

   if (exists("fqssnames")) {
     mff <- m
     ffqss <- paste(fqssnames, collapse = "+")
     mff$formula <- as.formula(paste(deparse(Terms), "+", ffqss))
   }

and here:

   if (length(qssterms) > 0) {
     X <- do.call(cbind,
                  c(list(X),
                    lapply(tmpc$vars, function(u) eval(parse(text = u), 
mff))))
     }

and the following line is extraneous:

    ef <- environment(formula)

That doesn't amount to much, and I haven't tested my substitute code 
beyond your example.

Best,
  John

John Fox, Professor Emeritus
McMaster University
Hamilton, Ontario, Canada
web: https://socialsciences.mcmaster.ca/jfox/

On 2020-09-21 9:40 a.m., Koenker, Roger W wrote:
> Here is a revised snippet that seems to work the way that was intended.  Apologies to anyone
> who wasted time looking at the original post.  Of course my interest in simpler or more efficient
> solutions remains unabated.
> 
> if (exists("fqssnames")) {
> 	mff <- m
> 	mff$formula <- Terms
>          ffqss <- paste(fqssnames, collapse = "+")
>          mff$formula <- as.formula(paste(deparse(mff$formula), "+", ffqss))
>      }
>      m$formula <- Terms
>      m <- eval(m, parent.frame())
>      mff <- eval(mff, parent.frame())
>      Y <- model.extract(m, "response")
>      X <- model.matrix(Terms, m)
>      ef <- environment(formula)
>      qss <- function(x, lambda) (x^lambda - 1)/lambda
>      if (length(qssterms) > 0) {
>          xss <- lapply(tmpc$vars, function(u) eval(parse(text = u), mff))
> 	for(i in 1:length(xss)){
> 	    X <- cbind(X, xss[[i]]) # Here is the problem
> 	}
>      }
> 
> 
>> On Sep 21, 2020, at 9:52 AM, Koenker, Roger W <rkoenker using illinois.edu> wrote:
>>
>> I need some help with a formula processing problem that arose from a seemingly innocuous  request
>> that I add a “subset” argument to the additive modeling function “rqss” in my quantreg package.
>>
>> I’ve tried to boil the relevant code down to something simpler as illustrated below.  The formulae in
>> question involve terms called “qss” that construct sparse matrix objects, but I’ve replaced all that with
>> a much simpler BoxCox construction that I hope illustrates the basic difficulty.  What is supposed to happen
>> is that xss objects are evaluated and cbind’d to the design matrix, subject to the same subset restriction
>> as the rest of the model frame.  However, this doesn’t happen, instead the xss vectors are evaluated
>> on the full sample and the cbind operation generates a warning which probably should be an error.
>> I’ve inserted a browser() to make it easy to verify that the length of xss[[[1]] doesn’t match dim(X).
>>
>> Any suggestions would be most welcome, including other simplifications of the code.  Note that
>> the function untangle.specials() is adapted, or perhaps I should say adopted form the survival
>> package so you would need the quantreg package to run the attached code.
>>
>> Thanks,
>> Roger
>>
>>
>>
>> fit <- function(formula, subset, data, ...){
>>     call <- match.call()
>>     m <- match.call(expand.dots = FALSE)
>>     tmp <- c("", "formula", "subset", "data")
>>     m <- m[match(tmp, names(m), nomatch = 0)]
>>     m[[1]] <- as.name("model.frame")
>>     Terms <- if(missing(data)) terms(formula,special = "qss")
>> 	    else terms(formula, special = "qss", data = data)
>>     qssterms <- attr(Terms, "specials")$qss
>>     if (length(qssterms)) {
>>         tmpc <- untangle.specials(Terms, "qss")
>>         dropx <- tmpc$terms
>>         if (length(dropx))
>>             Terms <- Terms[-dropx]
>>         attr(Terms, "specials") <- tmpc$vars
>> 	fnames <- function(x) {
>>             fy <- all.names(x[[2]])
>>             if (fy[1] == "cbind")
>>                 fy <- fy[-1]
>>             fy
>>         }
>>         fqssnames <- unlist(lapply(parse(text = tmpc$vars), fnames))
>>         qssnames <- unlist(lapply(parse(text = tmpc$vars), function(x) deparse(x[[2]])))
>>     }
>>     if (exists("fqssnames")) {
>>         ffqss <- paste(fqssnames, collapse = "+")
>>         ff <- as.formula(paste(deparse(formula), "+", ffqss))
>>     }
>>     m$formula <- Terms
>>     m <- eval(m, parent.frame())
>>     Y <- model.extract(m, "response")
>>     X <- model.matrix(Terms, m)
>>     ef <- environment(formula)
>>     qss <- function(x, lambda) (x^lambda - 1)/lambda
>>     if (length(qssterms) > 0) {
>>         xss <- lapply(tmpc$vars, function(u) eval(parse(text = u), m, enclos = ef))
>> 	for(i in 1:length(xss)){
>> 	    X <- cbind(X, xss[[i]]) # Here is the problem
>> 	}
>>     }
>>     browser()
>>     z <- lm.fit(X,Y) # The dreaded least squares fit
>>     z
>> }
>> # Test case
>> n <- 200
>> x <- sort(rchisq(n,4))
>> z <- rnorm(n)
>> s <- sample(1:n, n/2)
>> y <- log(x) + rnorm(n)/5
>> D = data.frame(y = y, x = x, z = z, s = (1:n) %in% s)
>> plot(x, y)
>> lam = 0.2
>> #f0 <- fit(y ~ qss(x,lambda = lam) + z, subset = s)
>> f1 <- fit(y ~ qss(x, lambda = lam) + z, subset = s, data = D)
>> ______________________________________________
>> R-help using r-project.org mailing list -- To UNSUBSCRIBE and more, see
>> https://stat.ethz.ch/mailman/listinfo/r-help
>> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
>> and provide commented, minimal, self-contained, reproducible code.
> 
> ______________________________________________
> R-help using r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
>



More information about the R-help mailing list