Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

165 lines (135 sloc) 4.751 kb
# Layout panels in a 2d grid.
#
# @params data list of data frames, one for each layer
# @params rows variables that form the rows
# @params cols variables that form the columns
# @return a data frame with columns \code{PANEL}, \code{ROW} and \code{COL},
# that match the facetting variable values up with their position in the
# grid
layout_grid <- function(data, rows = NULL, cols = NULL, margins = NULL, drop = TRUE, as.table = TRUE) {
if (length(rows) == 0 && length(cols) == 0) return(layout_null())
rows <- as.quoted(rows)
cols <- as.quoted(cols)
base_rows <- layout_base(data, rows, drop = drop)
if (!as.table) {
rev_order <- function(x) factor(x, levels = rev(ulevels(x)))
base_rows[] <- lapply(base_rows, rev_order)
}
base_cols <- layout_base(data, cols, drop = drop)
base <- df.grid(base_rows, base_cols)
# Add margins
base <- add_margins(base, list(names(rows), names(cols)), margins)
# Work around bug in reshape2
base <- unique(base)
# Create panel info dataset
panel <- id(base, drop = TRUE)
panel <- factor(panel, levels = seq_len(attr(panel, "n")))
rows <- if (is.null(names(rows))) 1L else id(base[names(rows)], drop = TRUE)
cols <- if (is.null(names(cols))) 1L else id(base[names(cols)], drop = TRUE)
panels <- data.frame(PANEL = panel, ROW = rows, COL = cols, base)
arrange(panels, PANEL)
}
# Layout out panels in a 1d ribbon.
#
# @params drop should missing combinations be excluded from the plot?
# @keywords internal
layout_wrap <- function(data, vars = NULL, nrow = NULL, ncol = NULL, as.table = TRUE, drop = TRUE) {
vars <- as.quoted(vars)
if (length(vars) == 0) return(layout_null())
base <- unrowname(layout_base(data, vars, drop = drop))
id <- id(base, drop = TRUE)
n <- attr(id, "n")
dims <- wrap_dims(n, nrow, ncol)
layout <- data.frame(PANEL = factor(id, levels = seq_len(n)))
if (as.table) {
layout$ROW <- as.integer((id - 1L) %/% dims[2] + 1L)
} else {
layout$ROW <- as.integer(dims[1] - (id - 1L) %/% dims[2])
}
layout$COL <- as.integer((id - 1L) %% dims[2] + 1L)
panels <- cbind(layout, unrowname(base))
panels[order(panels$PANEL), ]
}
layout_null <- function(data) {
data.frame(PANEL = 1, ROW = 1, COL = 1)
}
# Base layout function that generates all combinations of data needed for
# facetting
# The first data frame in the list should be the default data for the plot.
# Other data frames in the list are ones that are added to layers.
#
# @params data list of data frames (one for each layer)
# @keywords internal
layout_base <- function(data, vars = NULL, drop = TRUE) {
if (length(vars) == 0) return(data.frame())
# For each layer, compute the facet values
values <- compact(llply(data, quoted_df, vars = vars))
# Form the base data frame which contains all combinations of facetting
# variables that appear in the data
has_all <- unlist(llply(values, length)) == length(vars)
if (!any(has_all)) {
stop("At least one layer must contain all variables used for facetting")
}
base <- unique(ldply(values[has_all]))
if (!drop) {
base <- unique_combs(base)
}
# Systematically add on missing combinations
for (value in values[!has_all]) {
if (empty(value)) next;
old <- base[setdiff(names(base), names(value))]
new <- unique(value[intersect(names(base), names(value))])
if (drop) {
new <- unique_combs(new)
}
base <- rbind(base, df.grid(old, new))
}
if (is.null(base)) {
stop("Faceting variables must have at least one value")
}
base
}
ulevels <- function(x) {
if (is.factor(x)) {
x <- addNA(x, TRUE)
factor(levels(x), levels(x), exclude = NULL)
} else {
sort(unique(x))
}
}
unique_combs <- function(df) {
if (length(df) == 0) return()
unique_values <- llply(df, ulevels)
rev(expand.grid(rev(unique_values), stringsAsFactors = FALSE,
KEEP.OUT.ATTRS = TRUE))
}
df.grid <- function(a, b) {
if (nrow(a) == 0) return(b)
if (nrow(b) == 0) return(a)
indexes <- expand.grid(
i_a = seq_len(nrow(a)),
i_b = seq_len(nrow(b))
)
unrowname(cbind(
a[indexes$i_a, , drop = FALSE],
b[indexes$i_b, , drop = FALSE]
))
}
quoted_df <- function(data, vars) {
values <- eval.quoted(vars, data, emptyenv(), try = TRUE)
as.data.frame(compact(values))
}
# Arrange 1d structure into a grid
wrap_dims <- function(n, nrow = NULL, ncol = NULL) {
if (is.null(ncol) && is.null(nrow)) {
rc <- grDevices::n2mfrow(n)
nrow <- rc[2]
ncol <- rc[1]
} else if (is.null(ncol)) {
ncol <- ceiling(n / nrow)
} else if (is.null(nrow)) {
nrow <- ceiling(n / ncol)
}
stopifnot(nrow * ncol >= n)
c(nrow, ncol)
}
Jump to Line
Something went wrong with that request. Please try again.