2

Please consider this function:

tf <- function(formula = NULL, data = NULL, groups = NULL) {

    grv <- eval(substitute(groups), data, environment(formula)) # the values
    grn <- as.character(match.call()$groups) # the name
    gr <- match.call()$groups # unquoted name

    p <- xyplot(formula, data, # draws the data but not in groups
# Try these options:
#   p <- xyplot(formula, data, groups, # can't find 'cat2' 
#   p <- xyplot(formula, data, groups = data[,grn], # can't fine grn
#   p <- xyplot(formula, data, groups = grv, # can't find grv
        panel = function(x, y) {
            panel.stripplot(x, y, jitter.data = TRUE, pch = 20)
            }
            )
    p
    }

Which you can run with:

tf(formula = mpg~vs, groups = am, data = mtcars)

What am I doing wrong in passing the groups argument to xyplot - why can't it be found? I can't figure out how it wants the group information. Thanks.

Update:

@agstudy's answer is very helpful, but if I add the panel function as in the original example, the groups are still not recognized (no grouping, but no error occurs either):

tf <- function(formula = NULL, data = NULL, groups = NULL) {
    ll <- as.list(match.call(expand.dots = FALSE)[-1])
    p <- xyplot(as.formula(ll$formula), 
              data = eval(ll$data), 
              groups = eval(ll$groups),
                panel = function(x, y) {
                panel.stripplot(x, y, jitter.data = TRUE, pch = 20)
                }
                )
    p
    }

Something is still missing... Thanks.

Bryan Hanson
  • 6,055
  • 4
  • 41
  • 78
  • About whether this is a "feature" of lattice; I do believe this behavior is byproduct of the feature that allows one to put the name of a variable within the data set in as the `groups` parameter. This takes a little bit of R gymnastics and I think a byproduct is that things like you want to do here are more difficult. I know I've wrestled with this at least once before and wish I had more time right now to look into it again; it's an interesting question! – Aaron left Stack Overflow Feb 09 '13 at 15:14
  • Thx. As you know, these scoping issues vex me on a regular basis! – Bryan Hanson Feb 09 '13 at 15:37

2 Answers2

4

You can use eval here since match.call returns symbols.

tf <- function(formula = NULL, data = NULL, groups = NULL) {
  ll <- as.list(match.call(expand.dots = FALSE)[-1])
  p <- xyplot(as.formula(ll$formula), 
              data = eval(ll$data), 
              groups = eval(ll$groups),
              panel = function(x, y,...) { ## here ... contains groups and subscripts
                ## here you can transform x or y before giving them to the jitter
                panel.stripplot(x, y, jitter.data = TRUE, pch = 20,...)
              }
  )
  p
}
agstudy
  • 119,832
  • 17
  • 199
  • 261
  • Thank you, this is very helpful. Why do you think this is necessary? If I don't use `groups` formula and data are passed right into the plotting no problem. When I add `groups` a different technique must be used. In the `lattice` codes I see that `groups` is handled rather differently (and the handling is difficult to understand). Is there a short answer as to why? Also, I edited the original question, as in my real example I need to use that panel function (and several others). Thanks. – Bryan Hanson Feb 09 '13 at 13:53
  • @BryanHanson In my code groups is handled like other arguments. In your jitter panel you don't give grive the groups parameter. see my update. – agstudy Feb 09 '13 at 13:58
  • Yea, I just discovered myself the missing dots were needed. Thank you. – Bryan Hanson Feb 09 '13 at 14:01
  • I'm still curious/morbidly fascinated: In my original non-working function, a perfectly good argument value is not directly available for further use in another function inside the first one. Seems like a violation of some fundamental tenent. A 'feature' of lattice perhaps? – Bryan Hanson Feb 09 '13 at 14:42
  • (It's "tenet".) More supportively, I agree that lattice code can be confusing because some variables get abstracted and renamed so that they are panel-specific and end up named other than what you expect them to be. – IRTFM Feb 10 '13 at 02:09
2

One technique I use when I have trouble with scoping and calling functions within functions is to pass the parameters as strings and then construct the call within the function from those strings. Here's what that would look like here.

panel2 <- function(x, y, ...) {panel.stripplot(x, y, jitter.data = TRUE, pch = 20, ...)}
tf <- function(formula, data, groups) {
  eval(call("xyplot", as.formula(formula), 
                      groups=as.name(groups), 
                      data=as.name(data),
                      panel=as.name("panel2")))
}

tf("mpg~vs", "mtcars", "am") 

See this answer to one of my previous questions for another example of this: https://stackoverflow.com/a/7668846/210673.

Also see this answer to the sister question of this one, where I suggest something similar for use with aov: https://stackoverflow.com/a/14858614/210673

Community
  • 1
  • 1
Aaron left Stack Overflow
  • 36,704
  • 7
  • 77
  • 142