In your first example I get an error:
> mtest.data.frame(testdata, valid2=="N", valid3 > 1)
Error in mtest.data.frame(testdata, valid2 == "N", valid3 > 1) :
object 'valid2' not found
I expect the error because list(...) ought to evaluate the ... arguments.
Use substitute() to get the unevaluated ... arguments up front and
don't use substitute() in the loop over the elements of test.
There are several ways to get the unevaluated ... arguments. E.g.,
f0 <- function(x, ..., drop=FALSE) match.call(expand.dots=FALSE)$...
f1 <- function(x, ..., drop=FALSE) substitute(...())
f2 <- function(x, ..., drop=FALSE) as.list(substitute(list(...)))[-1]
Your function could be the following, where I also fixed a problem
with parent.frame() being
called in the wrong scope and improved, IMO, the names on the output data.frame.
m2 <- function (x, ..., drop = FALSE, verbose = FALSE)
{
tests <- substitute(...())
nms <- names(tests) # fix up names, since data.frame makes ugly ones
if (is.null(nms)) {
names(tests) <- paste0("T", seq_along(tests))
}
else if (any(nms == "")) {
names(tests)[nms == ""] <- paste0("T", which(nms == ""))
}
if (verbose) {
print(tests)
}
r <- if (length(tests) == 0) {
stop("no 'tests'")
}
else {
enclos <- parent.frame() # evaluate parent.frame() outside of FUN()
data.frame(lapply(tests, FUN=function(e) {
r <- eval(e, x, enclos)
if (!is.logical(r)) {
stop("'tests' must be logical")
}
r & !is.na(r)
}))
}
r
}
used as:
> m2(testdata, group2=="UNC", Eleven.Two=valid5=="11.2")
T1 Eleven.Two
1 TRUE FALSE
2 TRUE TRUE
3 FALSE FALSE
4 FALSE FALSE
5 FALSE FALSE
6 FALSE FALSE
7 TRUE FALSE
Bill Dunlap
TIBCO Software
wdunlap tibco.com
On Sat, Sep 6, 2014 at 3:31 PM, David Winsemius <[email protected]> wrote:
> The goal:
> to create a function modeled after `subset` (notorious for its
> non-standard evaluation) that will take a series of logical tests as
> unqiuoted expressions to be evaluated in the framework of a dataframe
> environment and return a dataframe of logicals:
>
>
> mtest.data.frame <-
> function (x, ..., drop=FALSE)
> { tests <- list(...); print(tests)
> r <- if (length(tests)==0)
> stop("no 'tests'")
> else { cbind.data.frame(
> lapply( tests, function(t){
> e <- substitute(t)
> r <- eval(e, x, parent.frame() )
> if ( !is.logical(r) ) {
> stop("'tests' must be logical") }
> r & !is.na(r) } ) )
> }
> }
> #--------------
>
> testdata <- structure(list(group1 = structure(1:7, .Label = c("Group A",
> "Group B", "Group C", "Group D", "Group E", "Group F", "Group G"
> ), class = "factor"), group2 = structure(c(3L, 3L, 2L, 1L, 1L,
> 2L, 3L), .Label = c("LS", "SS", "UNC"), class = "factor"), valid1 =
> structure(c(2L,
> 1L, NA, 1L, 2L, 2L, 1L), .Label = c("N", "Y"), class = "factor"),
> valid2 = structure(c(1L, 1L, 2L, 1L, 1L, 2L, 1L), .Label = c("N",
> "Y"), class = "factor"), valid3 = structure(c(4L, 3L, NA,
> 2L, 1L, NA, 5L), .Label = c("0.3", "0.7", "1.2", "1.4", "1.7"
> ), class = "factor"), valid4 = structure(c(2L, 1L, 3L, 4L,
> 1L, 1L, 5L), .Label = c("0.3", "0.4", "0.53", "0.66", "0.71"
> ), class = "factor"), valid5 = structure(c(4L, 1L, NA, NA,
> 3L, NA, 2L), .Label = c("11.2", "11.7", "8.3", "8.5"), class =
> "factor")), .Names = c("group1",
> "group2", "valid1", "valid2", "valid3", "valid4", "valid5"), row.names = c(NA,
> -7L), class = "data.frame")
>
> #######
>
>
>> mtest.data.frame(testdata, valid2=="N", valid3 > 1)
> [[1]]
> [1] "tests are"
>
> [[2]]
> [1] TRUE TRUE FALSE TRUE TRUE FALSE TRUE
>
> [[3]]
> [1] TRUE TRUE NA FALSE FALSE NA TRUE
>
> This actually seemed to be somewhat successful, but when ...
>
> Now if I take out the `print()` call for 'tests', I get an different answer:
>
>> mtest.data.frame <-
> + function (x, ..., drop=FALSE)
> + { tests <- list(...)
> + r <- if (length(tests)==0)
> + stop("no 'tests'")
> + else { cbind.data.frame(
> + lapply( tests, function(t){
> + e <- substitute(t)
> + r <- eval(e, x, parent.frame() )
> + if ( !is.logical(r) ) {
> + stop("'tests' must be logical") }
> + r & !is.na(r) } ) )
> + }
> + }
>> mtest.data.frame(testdata, valid2=="N", valid3 > 1)
>> # i.e. no answer
>
> --
>
> David Winsemius
> Alameda, CA, USA
>
> ______________________________________________
> [email protected] 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.
______________________________________________
[email protected] 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.