#' Stack overlapping objects which can be shifted vertically or horizontally
#'
#' @param vjust Vertical adjustment for geoms that have a position
#' (like points or lines), not a dimension (like bars or areas). Set to
#' `0` to align with the bottom, `0.5` for the middle,
#' and `1` (the default) for the top.
#' @param vexpand numeric, distance to be shifted vertically for geoms that
#' have a position, default is NA.
#' @param hexpand numeric, distance to be shifted horizontally for geoms that
#' have a position, default is NA.
#' @param reverse If `TRUE`, will reverse the default stacking order.
#' This is useful if you're rotating both the plot and legend.
#' @return position method.
#' @importFrom ggplot2 ggproto
#' @author Shuangbin Xu
#' @export
#' @examples
#' library(ggplot2)
#' library(patchwork)
#' df <- data.frame(trt = c("a", "b", "c"), outcome = c(2.3, 1.9, 3.2))
#' #
#' p1 <- ggplot(df, aes(x=trt, y=outcome)) +
#' geom_bar(stat="identity",
#' position=position_stackx())
#'
#' p2 <- ggplot(df, aes(x=trt, y=outcome)) +
#' geom_bar(stat="identity",
#' position=position_stackx(vexpand=5))
#'
#' p3 <- ggplot(df, aes(x=outcome, y=trt)) +
#' geom_bar(stat="identity",
#' orientation="y",
#' position=position_stackx(hexpand=5))
#' p <- p1 + p2 + p3
#' p
position_stackx <- function(vjust = 1, hexpand=NA, vexpand=NA, reverse = FALSE) {
ggproto(NULL, PositionStackx, vjust = vjust, reverse = reverse,
hexpand=hexpand, vexpand=vexpand)
}
#' PositionStackx
#' @importFrom ggplot2 ggproto Position has_flipped_aes flip_data
#' @importFrom ggplot2 remove_missing
#' @rdname ggplot2-ggproto
#' @format NULL
#' @usage NULL
#' @author Shuangbin Xu
PositionStackx <- ggproto("PositionStackx", Position,
type = NULL,
vjust = 1,
fill = FALSE,
reverse = FALSE,
hexpand = NA,
vexpand = NA,
setup_params = function(self, data) {
flipped_aes <- has_flipped_aes(data)
data <- flip_data(data, flipped_aes)
list(
var = self$var %||% stack_var(data),
fill = self$fill,
vjust = self$vjust,
reverse = self$reverse,
flipped_aes = flipped_aes,
hexpand = self$hexpand,
vexpand = self$vexpand
)
},
setup_data = function(self, data, params) {
data <- flip_data(data, params$flipped_aes)
if (is.null(params$var)) {
return(data)
}
data$ymax <- switch(params$var,
y = data$y,
ymax = ifelse(data$ymax == 0, data$ymin, data$ymax)
)
data <- remove_missing(
data,
vars = c("x", "xmin", "xmax", "y"),
name = "position_stackx"
)
flip_data(data, params$flipped_aes)
},
compute_panel = function(data, params, scales) {
data <- flip_data(data, params$flipped_aes)
if (is.null(params$var)) {
return(data)
}
negative <- data$ymax < 0
negative[is.na(negative)] <- FALSE
neg <- data[negative, , drop = FALSE]
pos <- data[!negative, , drop = FALSE]
if (any(negative)) {
neg <- collide(neg, NULL, "position_stackx", pos_stack,
vjust = params$vjust,
fill = params$fill,
reverse = params$reverse
)
}
if (any(!negative)) {
pos <- collide(pos, NULL, "position_stackx", pos_stack,
vjust = params$vjust,
fill = params$fill,
reverse = params$reverse
)
}
data <- rbind(neg, pos)[match(seq_len(nrow(data)), c(which(negative), which(!negative))),]
data <- flip_data(data, params$flipped_aes)
if (!is.na(params$vexpand)){
data$ymin <- data$ymin + params$vexpand
data$ymax <- data$ymax + params$vexpand
}
if (!is.na(params$hexpand)){
data$xmin <- data$xmin + params$hexpand
data$xmax <- data$xmax + params$hexpand
}
data #<- data.frame(data, check.names=FALSE)
}
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.