#' Evaluate a formula
#'
#' @param formula a formula (` y ~ x | z`) to evaluate
#' @param data a data frame or environment in which evaluation occurs
#' @return a list containing data frames corresponding to the left, right, and condition
#' slots of `formula`
#' @param ops a vector of operator symbols allowable to separate variables in rhs
#'
#' @param subset an optional vector describing a subset of the observations to be used.
#' Currently only implemented when data is a data frame.
#'
#' @examples
#' if (require(mosaicData)) {
#' data(CPS85)
#' cps <- CPS85[1:6,]
#' cps
#' evalFormula(wage ~ sex & married & age | sector & race, data=cps)
#' }
#' @export
evalFormula <- function(formula, data=parent.frame(), subset, ops=c('+','&')) {
# could make this an S4 object instead
# core of subset() copied into here
if (!missing(subset) && is.data.frame(data)) {
e <- substitute(subset)
r <- eval(e, data, parent.frame())
if (!is.logical(r))
stop("'subset' must evaluate to logical")
r <- r & !is.na(r)
index <- which( rep(r, out.length=nrow(data)) )
data <- data[r, , drop = FALSE]
} else {
index <- NA
}
result <- list(
left = evalSubFormula( lhs(formula), ops=ops, data, env=environment(formula)),
right = evalSubFormula( rhs(formula), ops=ops, data, env=environment(formula)),
condition = evalSubFormula(condition(formula), ops=ops, data, env=environment(formula)),
index = index
)
return(result)
}
# evalSubFormula and evalFormula could be made methods with a common generic
#' Evaluate a part of a formula
#'
#' @param x an object appearing as a subformula (typically a name or a call)
#' @param data a data frame or environment in which things are evaluated
#' @param ops a vector of operators that are not evaluated as operators but
#' instead used to further split `x`
#' @param env an environment in which to search for objects not in `data`.
#' @return a data frame containing the terms of the evaluated subformula
#' @examples
#' if (require(mosaicData)) {
#' data(CPS85)
#' cps <- CPS85[1:6,]
#' cps
#' evalSubFormula( rhs( ~ married & sector), data=cps )
#' }
#' @export
evalSubFormula <- function(x, data=NULL, ops=c('+','&'), env=parent.frame()){
sx <- substitute(x)
if (is.null(x)) return(NULL)
if( is.name(x) || !(as.character(x[[1]]) %in% ops) ) {
res <- data.frame(eval(x, data, env), stringsAsFactors=FALSE)
names(res) <- deparse(x)
return( res )
}
else return(joinFrames( evalSubFormula(x[[2]],data), evalSubFormula(x[[3]],data)))
}
#' Join data frames
#'
#' @param left,right data frames
#' @param \dots data frames to be joined
#' @rdname joinFrames
#' @return a data frame containing columns from each of data frames being joined.
#' @export
joinFrames <- function(...) {
dots <- list(...)
if (length(dots) == 0) return(NULL)
if (length(dots) == 1) return(dots[[1]])
if (length(dots) == 2) return(joinTwoFrames(dots[[1]],dots[[2]]))
first <- dots[[1]]; dots[[1]] <- NULL
return( joinTwoFrames( first, do.call(joinFrames, dots)) )
}
#' @rdname joinFrames
joinTwoFrames <- function(left, right){
if( is.null(right)) return(left)
if( is.null(left)) return(right)
# this is to keep names like "cross(sex,hair)" intact
result <- data.frame(left, right)
names(result) <- c((names(left)),(names(right)))
return(result)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.