On 6/8/07, Zack Weinberg <zackw at panix.com>
wrote:> As you may not be surprised to hear, no sooner did I post the previous
> message than I realized I had a really dumb mistake. I've now gotten
> a bit farther but am still stuck. New code:
>
> graph <- function (x, data, groups, xlab) {
> pg <- function(x, y, group.number, ...) fnord
> body(pg) <- substitute({
> panel.xyplot(x, y, ..., group.number=group.number)
> panel.text(2, unique(y[x==2]),
> levels(G)[group.number],
> pos=4, cex=0.5)
> }, list(G=eval(substitute(groups), data, parent.frame())))
>
> print(xyplot(x, data=data, groups=substitute(groups),
> type='l',
> ylab=list(cex=1.1, label='Mean RT (ms)'),
> xlab=list(cex=1.1, label=xlab),
> scales=list(
> x=list(alternating=c(1,1), tck=c(1,0)),
> y=list(alternating=c(1,0))
> ),
> panel=panel.superpose,
> panel.groups=pg
> ))
> }
>
> Questions:
> 1) The "groups=substitute(groups)" bit (in the call to xyplot)
still
> doesn't work. As far as I can tell, xyplot wants the *symbol* which
> is the name of the factor (in the data frame) to group by.
> The above seems to wind up passing it the symbol "groups", which
> causes the prepanel function to barf. I have not been able to find
> any way to evaluate one layer of "groups" to get me the symbol
passed
> in, rather than the value of that symbol. Am I right? How do I give
> it what it wants?
>
> 2) Why do I have to do that stupid dance with replacing the body of
> pg? The documentation leads me to believe this is a lexically scoped
> language, shouldn't it be able to pick G out of the enclosing frame?
This is all a consequence of non-standard evaluation, which can be a
real pain sometimes. I don't have a solution that is "intuitive",
but
I don't think there is one. This is what I would do (and there are
examples of this in lattice, e.g. see lattice:::dotplot.formula):
graph <- function (x, data, groups, xlab) {
## set up g and pg (lexical scope does work here)
g <- eval(substitute(groups), data, parent.frame())
pg <- function(x, y, group.number, ...) {
panel.xyplot(x, y, ..., group.number=group.number)
panel.text(2, unique(y[x==2]),
levels(g)[group.number],
pos=4, cex=0.5)
}
## modify and evaluate call without
## actually evaluating 'groups'
ccall <- match.call()
ccall[[1]] <- quote(xyplot)
fixed.args <-
list(type='l',
ylab=list(cex=1.1, label='Mean RT (ms)'),
xlab=list(cex=1.1, label=xlab),
scales=list(
x=list(alternating=c(1,1), tck=c(1,0)),
y=list(alternating=c(1,0))
),
panel="panel.superpose",
panel.groups = pg)
ccall[names(fixed.args)] <- fixed.args
eval.parent(ccall)
}
sm <- data.frame(x = 1:10, y = rnorm(10), a = gl(3, 1, 10))
graph(y ~ x, sm, a, "foo")
This is a different approach from the one you were trying, but I think
it makes more sense once you get used to it. Note that there are some
subtle things going on here. The 'g' used by pg() is available only
because the relevant environment is stored and is accessible through
the "trellis" object:
> foo <- graph(y ~ x, sm, a, "foo")
> ls(environment(foo$panel.args.common$panel.groups))
[1] "ccall" "data" "fixed.args"
"g" "groups"
[6] "pg" "x" "xlab"
Hope this helps,
-Deepayan