Last active
April 23, 2016 13:55
-
-
Save expersso/41922d764d82f0637490d2b7422d8350 to your computer and use it in GitHub Desktop.
Creating stacked barchar in ggplot with positive and negative values
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| library(ggplot2) | |
| df <- data.frame( | |
| "sector" = c("a", "b", "c"), | |
| "date" = rep(as.Date(c("1998-01-01", "1999-01-01", "2000-01-01")), each = 3), | |
| "value" = c(-1, 1, 1, 1, -1, -3, 1.3, 1, 2) | |
| ) | |
| p <- ggplot(df, aes(x = date, y = value, fill = sector)) | |
| # Doesn't plot bars properly | |
| p + | |
| geom_bar(stat = "identity") + | |
| geom_hline(yintercept = 0) | |
| # Warning message: | |
| # Stacking not well defined when ymin != 0 | |
| # See e.g. | |
| # http://stackoverflow.com/questions/13734368/ggplot2-and-a-stacked-bar-chart-with-negative-values | |
| # DOES plot bars properly, but involves code duplication, etc | |
| p + | |
| geom_bar(stat = "identity", data = subset(df, value >= 0)) + | |
| geom_bar(stat = "identity", data = subset(df, value < 0)) + | |
| geom_hline(yintercept = 0) | |
| # GOAL: make this work like expression above | |
| ### p + geom_twoway_bar() | |
| # Incredibly kludgey and hacky solution | |
| ## Wrapper around ggplot2's `+` operator | |
| `+.gg` <- function (e1, e2) { | |
| e2name <- substitute(e2) | |
| dots <- as.list(e2name)[-1] | |
| dots <- append(dots, list(gg_object = e1)) | |
| if (deparse(e2name[[1]]) == "geom_twoway_bar") | |
| do.call(geom_twoway_bar, dots) | |
| else | |
| ggplot2:::`+.gg`(e1, e2) | |
| } | |
| geom_twoway_bar <- function(gg_object = NULL, ...) { | |
| if (ggplot2::is.ggplot(gg_object)) { | |
| p_obj <- eval(gg_object, envir = parent.frame()) | |
| y_var <- p_obj$data[, deparse(eval(substitute(p_obj$mapping$y)))] | |
| geom_pos <- ggplot2::geom_bar(stat = "identity", | |
| data = p_obj$data[y_var >= 0, , drop = FALSE], | |
| ...) | |
| geom_neg <- ggplot2::geom_bar(stat = "identity", | |
| data = p_obj$data[y_var < 0, , drop = FALSE], | |
| ...) | |
| hline <- ggplot2::geom_hline(yintercept = 0) | |
| Reduce(`+`, c(geom_pos, geom_neg, hline), init = p_obj) | |
| } | |
| } | |
| # This now works | |
| p + geom_twoway_bar() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
That's a pretty succinct and expressive way to do this. The problem with making it a full-on
ggaltGeomlies in this bit of ggplo2 code: https://github.com/hadley/ggplot2/blob/59c503b8e1cacf1f9264d1e233b7a305916905d6/R/position-collide.r#L57-L72. It'd be super easy to do something like (quick hack):But the stacking operation happens after the call to
setup_data()and totally destroys that hard work.collide()is called by ggplot2 internally, so it can't be easily overriden by an external pkg. But, it may be possible to make a newPositionobject. I'll poke at that.