Skip to content

evaluating expressions with sub expressions

13 messages · Baptiste Auguie, Gabor Grothendieck, William Dunlap +3 more

#
Hallo

I'm having trouble figuring out how to evaluate an expression when one of
the variables in the expression is defined separately as a sub expression.
Here's a simplified example

mat <- expression(0, f1*s1*g1)  # vector of formulae
g1 <- expression(1/Tm)          # expansion of the definition of g1
vals <- data.frame(f1=1, s1=.5, Tm=2) # one set of possible values for
variables

before adding this sub expression I was using the following to evaluate "mat"

sapply(mat, eval, vals)

Obviously I could manually substitute in 1/Tm for each g1 in the
definition of "mat", but the actual expression vector is much longer, and
the sub expression more complicated. Also, the subexpression is often
adjusted for different scenarios.  Is there a simple way of changing this
or redefining "mat" so that I can define "g1" like a macro to be used in
the expression vector.

Thanks!
Jennifer
#
Hi,

Would this do as an alternative syntax?

g1 <- quote(1/Tm)
mat <- list(0, bquote(f1*s1*.(g1)))
vals <- data.frame(f1=1, s1=.5, Tm=2)

sapply(mat, eval, vals)

HTH,

baptiste


On 29 January 2010 17:51, Jennifer Young
<Jennifer.Young at math.mcmaster.ca> wrote:
#
Hmm

I *think* this will work, but may break in a further sub routine.
It certainly works in this example, but my expression vector is used in
many scenarios and it will take a while to check them all.

For instance, I take the derivative of each element with respect to each
variable using

sapply(mat, deriv, names(vals))

This bit seems to still work, but I'd welcome a solution that doesn't
change the structure of the expression vector to a list, just in case.

Thanks for this solution.
#
The following recursively walks the expression tree.  The esub
function is from this page (you may wish to read that entire thread):
http://tolstoy.newcastle.edu.au/R/help/04/03/1245.html

esub <- function(expr, sublist) do.call("substitute", list(expr, sublist))

proc <- function(e, env = parent.frame()) {
   for(nm in all.vars(e)) {
      if (exists(nm, env) && is.language(g <- get(nm, env))) {
         if (is.expression(g)) g <- g[[1]]
            g <- Recall(g, env)
            L <- list(g)
            names(L) <- nm
             e <- esub(e, L)
	  }
        }
     e
}

mat <- expression(0, f1*s1*g1)
g1 <- expression(1/Tm)
vals <- data.frame(f1=1, s1=.5, Tm=2)
e <- sapply(mat, proc)
sapply(e, eval, vals)

The last line should give:
[1] 0.00 0.25


On Fri, Jan 29, 2010 at 11:51 AM, Jennifer Young
<Jennifer.Young at math.mcmaster.ca> wrote:
#
Folks:

Stripped to its essentials, Jennifer's request seemed simple: substitute a
subexpression as a named variable for a variable name in an expression, also
expressed as a named variable. A simple example is:
The task is to "substitute" the expression in z1, "1/t", for "b" in e,
yielding the substituted expression as the result.

Gabor provided a solution, but it seemed to me like trying to swat a fly
with a baseball bat -- a lot of machinery for what should be a more
straightforward task. Of course, just because I think it **should be**
straightforward does not mean it actually is. But I fooled around a bit
(guided by Gabor's approach and an old Programmer's Niche column of Bill
Venables) and came up with:
[[1]]
[1] 0

[[2]]
a * (1/t)
expression(0, a * (1/t))
[1] 0.6666667

Now you'll note that to do this I explicitly used quote() to produce the
variable holding the subexpression to be substituted. You may ask, why not
use expression() instead, as in
This doesn't work:
[[1]]
[1] 0

[[2]]
a * expression(1/t)
## Yielding ...
expression(0, a * expression(1/t)) #### Not what we want! 
## And sure enough ...
Error in a * expression(1/t) : non-numeric argument to binary operator

I think I understand why the z <- expression() approach does not work; but I
do not understand why the z <- quote() approach does! The mode of the return
from both of these is "call", but they are different (because identical()
tells me so). Could someone perhaps elaborate on this a bit more? And is
there a yet simpler and more straightforward way to do the above than what I
proposed?

Cheers,

Bert Gunter
Genentech Nonclinical Statistics


-----Original Message-----
From: r-help-bounces at r-project.org [mailto:r-help-bounces at r-project.org] On
Behalf Of Gabor Grothendieck
Sent: Friday, January 29, 2010 11:01 AM
To: Jennifer Young
Cc: r-help at r-project.org
Subject: Re: [R] evaluating expressions with sub expressions

The following recursively walks the expression tree.  The esub
function is from this page (you may wish to read that entire thread):
http://tolstoy.newcastle.edu.au/R/help/04/03/1245.html

esub <- function(expr, sublist) do.call("substitute", list(expr, sublist))

proc <- function(e, env = parent.frame()) {
   for(nm in all.vars(e)) {
      if (exists(nm, env) && is.language(g <- get(nm, env))) {
         if (is.expression(g)) g <- g[[1]]
            g <- Recall(g, env)
            L <- list(g)
            names(L) <- nm
             e <- esub(e, L)
	  }
        }
     e
}

mat <- expression(0, f1*s1*g1)
g1 <- expression(1/Tm)
vals <- data.frame(f1=1, s1=.5, Tm=2)
e <- sapply(mat, proc)
sapply(e, eval, vals)

The last line should give:
[1] 0.00 0.25


On Fri, Jan 29, 2010 at 11:51 AM, Jennifer Young
<Jennifer.Young at math.mcmaster.ca> wrote:
"mat"
http://www.R-project.org/posting-guide.html
______________________________________________
R-help at r-project.org mailing list
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.
#
You asked
   And is there a yet simpler and more
   straightforward way to do the 
   above than what I proposed?

You could use S+, whose substitute() function
   (a) descends into expressions and functions
   (b) has an argument (evaluate=TRUE) so you
       don't need to use do.call when the first
       argument is not a literal
E.g.,
   > e <- expression(a*b, function(x)x+b, log(b))
   > substitute(e, list(b=Quote(exp(1))), evaluate=TRUE)
   expression(a * exp(1), function(x)
   x + exp(1), log(exp(1)))

Bill Dunlap
Spotfire, TIBCO Software
wdunlap tibco.com
#
If its good enough to have one level of substitution then esub in my
post (originally due to Tony Plate -- see reference in my post) is all
that is needed:

esub(mat[[2]], list(g1 = g1[[1]]))

but I think the real problem could require multiple levels of
substitution in which case repeated application of esub is needed as
you walk the expression tree which is what proc() in my post does.

For example, suppose mat[[2]] is a function of g1 which is a function
of Tm which is a function of z.  Then continuing the example in the
original post this does the repeated substitution needed (which would
be followed by an eval, not shown here, as in my original post):
[[1]]
[1] 0

[[2]]
f1 * s1 * (1/z^2)

To answer your question, quote() produces a call object but expression
produces a call wrapped in an expression which is why there is special
handling of expression objects in the proc() function in my post.
On Fri, Jan 29, 2010 at 4:38 PM, Bert Gunter <gunter.berton at gene.com> wrote:
#
Inline below...

-----Original Message-----
From: Gabor Grothendieck [mailto:ggrothendieck at gmail.com] 
Sent: Friday, January 29, 2010 2:12 PM
To: Bert Gunter
Cc: Jennifer Young; r-help at r-project.org
Subject: Re: [R] evaluating expressions with sub expressions

If its good enough to have one level of substitution then esub in my
post (originally due to Tony Plate -- see reference in my post) is all
that is needed:

esub(mat[[2]], list(g1 = g1[[1]]))

--- Yes, this is essentially what I did using lapply

but I think the real problem could require multiple levels of
substitution in which case repeated application of esub is needed as
you walk the expression tree which is what proc() in my post does.

-- Indeed. But my point was to handle the simple case simply.


To answer your question, quote() produces a call object but expression
produces a call wrapped in an expression which is why there is special
handling of expression objects in the proc() function in my post.

-- Aha! And of course I should have realized that I could have easily
determined this myself with:
[[1]]
`*`

[[2]]
a

[[3]]
b

## But ...
[[1]]
a * b
[1] "call"
[[1]]
`*`

[[2]]
a

[[3]]
b

So now all is clear to me.

Thanks to both Bill and Gabor for their informative replies.

-- Bert

Bert Gunter
Genentech Nonclinical Statistics
On Fri, Jan 29, 2010 at 4:38 PM, Bert Gunter <gunter.berton at gene.com> wrote:
also
I
return
I
On
expression.
http://www.R-project.org/posting-guide.html
#
Thanks so much everyone!

Bert's shorted example does what I need, but I'm filing away Gabor's
solution for when I inevitably need it some day.   I've never found the
handling of variables in R to be very straightforward; sometimes I pine
for Maple to do my algebra for me...
#
On Sat, Jan 30, 2010 at 10:39 AM, Jennifer Young
<Jennifer.Young at math.mcmaster.ca> wrote:
There are several interfaces to Computer Algebra Systems in R.  Try
this (but read instructions on home page
  http://ryacas.googlecode.com
first particularly the part about ensuring you have the right version
of XML installed).  Below, Sym creates a symbolic variable:
Loading required package: XML
[1] "Starting Yacas!"
expression(f1 * s1/Tm)
1 day later
#
(For R language geeks only)

Folks:

I think the best solution for the issue in the Subject line(see 29 January
thread on this for details) was the one Jennifer and Gabor previously
arrived at: (essentially)don't use R; instead, use a computer algebra system
that you can access through an R interface (e.g. Ryacas).

HOWEVER, I still wondered whether one could come up with a "simple" pure R
solution for simple but more general cases. What I offered previously was
too simple for the general case, as Gabor pointed out. It could do:

substitute the expression, "1/t" for "b" in the expression a*b;

but it could not descend further to handle:

substitute the expression, "1/t" for "b" in the expression a*b, where the
expression "sin(z+3)" is in turn to be substituted for t.

So I fooled with this a bit further and **think** (with trepidation) I found
that my previous approach does seem to extend to the general case by just
repeatedly processing the expression until done. One can even do this
without recursion (as it's tail recursion only) as follows (but see the
caveat below):

esub <- function(expr,sublist)
{
   nm <- names(sublist)
   while(length(intersect(all.vars(expr),nm))){
      sub<- lapply(sublist, function(x)if(is.expression(x))x[[1]] else x)
      expr
<-as.expression(lapply(expr,function(x)do.call(substitute,list(x,sub))))
   }
   expr
}

It can be used as:

Example 1:
expression(1/t + x^2)

CAVEAT: Note that all the expressions to be substituted can be formed by
either quote() or expression(), ** but they must be explicitly given in a
list.** In particular, they will not automatically be looked up for in the
frame of the call, as was the case with Gabor's/Tony Plate's version.

Example 2:
expression(1/exp(y) + (exp(y) + 3)^2)

## So now the substitutions properly extend into subexpressions.


Example 3:
## Of course, this also works:
expression(1/exp(y) + (exp(y)^2 + 3)^2)

I would appreciate being told (on list) if/how this scheme can be broken or
a reference to other approaches (besides that which Gabor provided, of
course). Clever improvements are also always welcome.

Cheers to all,

Bert Gunter
Genentech Nonclinical Statistics
#
On Sun, 31 Jan 2010, Bert Gunter wrote:

            
This is more or less what bquote() does, and it works, recursively, in a few lines of pure R (it just doesn't solve your particular problem because it substitutes for .() rather than for b).

     -thomas

Thomas Lumley			Assoc. Professor, Biostatistics
tlumley at u.washington.edu	University of Washington, Seattle
#
Thankyou Thomas. Yes, I was motivated by bquote(). It is sooooo slick (to
some extent, still too slick for me). I wanted to do it nonrecursively
because it's both clearer (I think) and maybe even more efficient (though I
doubt this is detectable in any case). 

Bert Gunter
Genentech Nonclinical Statistics

-----Original Message-----
From: Thomas Lumley [mailto:tlumley at u.washington.edu] 
Sent: Sunday, January 31, 2010 2:57 PM
To: Bert Gunter
Cc: 'Gabor Grothendieck'; 'Jennifer Young'; r-help at r-project.org
Subject: Re: [R] (With trepidation): Evaluating expressions with sub
expressions again
On Sun, 31 Jan 2010, Bert Gunter wrote:

            
system
found
This is more or less what bquote() does, and it works, recursively, in a few
lines of pure R (it just doesn't solve your particular problem because it
substitutes for .() rather than for b).

     -thomas

Thomas Lumley			Assoc. Professor, Biostatistics
tlumley at u.washington.edu	University of Washington, Seattle