Nothing
#' @title Aggregate, an Alternative \link[stats]{formula}-Interface
#'
#' @description
#' An alternative aggregation function with a \link[stats]{formula}-interface,
#' to avoid the \link[base]{cbind}-operation in the function \link[stats]{aggregate.formula}.
#'
#' @param data a \link[base]{data.frame}
#'
#' @param by a two-sided \link[stats]{formula}
#'
#' @param ... additional parameters of the function \link[stats]{aggregate.data.frame},
#' *except for* `simplify`
#'
#' @details
#' The \link[base]{cbind}-operation in the function \link[stats]{aggregate.formula}
#' messes up with column(s) that are
#' \describe{
#' \item{\link[base]{factor}}{and treat them as \link[base]{integer}}
#' \item{\link[survival]{Surv}}{and treat them as \link[base]{matrix}}
#' }
#'
#' The function \link[stats]{aggregate.data.frame} only accepts
#' a \link[base]{list} of \link[base]{factor}s for the parameter `by`.
#'
#' Therefore, the function [aggregate2()] is created to take care of
#' the \link[base]{factor} and \link[survival]{Surv} columns of the input,
#' with a \link[stats]{formula}-interface.
#'
#' @returns
#' The function [aggregate2()] returns a \link[base]{data.frame}.
#'
#' @note
#' The function \link[stats]{aggregate.data.frame} is the workhorse of
#' the function \link[stats]{aggregate.formula}.
#'
#' The function \link[spatstat.geom]{as.hyperframe.data.frame}
#' is **designed** to handle the \link[base]{list}-columns
#' returned by the function \link[stats]{aggregate}.
#'
#' @importFrom stats aggregate.data.frame model.frame.default update.formula
#' @export
aggregate2 <- function(data, by, ...) {
# drop unused factor levels in all columns of `data`
data[] <- data |>
lapply(FUN = \(i) {
if (!is.factor(i)) return(i)
factor(i) # drop empty levels!!
})
if (!is.call(by) || (by[[1L]] != '~') || (length(by) != 3L)) stop('`by` must be two-sided formula')
if (is.symbol(by[[2L]]) && (by[[2L]] == '.')) {
vars <- names(data)
} else if (is.call(by[[2L]]) && (by[[2L]][[1L]] == '-')) {
# e.g. `by = . - x1 - x2 ~ subj_id/image_id`
vars <- names(data) |>
lapply(FUN = as.symbol) |>
Reduce(f = \(e1, e2) call(name = '+', e1, e2)) |>
call(name = '~', . = _) |>
eval() |>
update.formula(new = call(name = '~', by[[2L]])) |>
all.vars()
} else {
vars <- all.vars(by)
if (!all(vars %in% names(data))) stop()
}
out <- data[unique(c(all.vars(by[[3L]]), vars))]
f <- by[[3L]] |>
call(name = '~', . = _) |>
model.frame.default(formula = _, data = data) |>
as.list.data.frame() |>
interaction(drop = TRUE, lex.order = TRUE)
if (all(table(f) == 1L)) return(out) # exception handling, no aggregation needed!!
z <- out |>
aggregate.data.frame(
x = _,
by = list(.f = f), ...,
simplify = TRUE # must!! for `Surv`-column!!
)
z[] <- z |>
lapply(FUN = unsimplify)
z <- z[-1L] # grouping structure on the 1st column removed
return(z)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.