#' Plot a time series as a horizon plot #' #' A horizon plot breaks the Y dimension down using colours. This is useful #' when visualising y values spanning a vast range and / or trying to highlight #' outliers without losing context of the rest of the data.\cr \cr Horizon #' plots are best viewed in an apsect ratio of very low vertical length. #' #' @md #' @section Aesthetics: `x`, `y`, `fill`. `fill` defaults to `..band..` which is #' the band number the current data fill area belongs in. #' @section Other parameters: `bandwidth`, to dictate the span of a band. #' @export geom_horizon <- function(mapping = NULL, data = NULL, show.legend = TRUE, inherit.aes = TRUE, na.rm = TRUE, bandwidth = NULL, ...) { list( layer( data = data, mapping = mapping, stat = "horizon", geom = GeomHorizon, position = 'identity', show.legend = show.legend, inherit.aes = inherit.aes, params = list(bandwidth = bandwidth, na.rm = na.rm, ...) ) ) } #' @rdname geom_horizon #' @keywords internal #' @export GeomHorizon <- ggproto("GeomHorizon", GeomArea, required_aes = c("x", "y"), default_aes = plyr::defaults( aes(fill=NA, size = 0.15, linetype = 1, alpha = NA, colour = "gray20"), ggplot2::GeomArea$default_aes ), draw_key = ggplot2::draw_key_rect ) #' Transforms data for a horizon plot #' @rdname geom_horizon #' @export stat_horizon <- function(mapping = NULL, data = NULL, geom = "horizon", show.legend = TRUE, inherit.aes = TRUE, na.rm = TRUE, bandwidth = NULL, ...) { list( layer( stat = StatHorizon, data = data, mapping = mapping, geom = geom, position = 'identity', show.legend = show.legend, inherit.aes = inherit.aes, params = list(bandwidth = bandwidth, na.rm = na.rm, ...) ) ) } #' @rdname geom_horizon #' @keywords internal #' @export StatHorizon <- ggproto( "StatHorizon", Stat, required_aes = c("x", "y"), default_aes = aes(fill=..band..), setup_params = function(data, params) { # calculating a default bandwidth if (is.null(params$bandwidth)) { params$bandwidth <- diff(range(data$y)) / 4 message(sprintf("bandwidth not specified. Using computed bandwidth %s", params$bandwidth)) } params$n_min_y <- min(data$y, na.rm = TRUE) params }, compute_group = function(data, scales, bandwidth, n_min_y) { # calculating the band in which the values fall data$fillb <- ((data$y - n_min_y) %/% bandwidth) + 1 # calculating the banded y value orig_y <- data$y orig_fill_b <- data$fillb data$y <- data$y - (bandwidth * (data$fillb - 1)) - n_min_y fill_bands <- sort(unique(data$fillb)) # for each band, calculating value at a particular x banded_data <- lapply( fill_bands, function(i_fill_band) { df_banded_data <- data[data$fillb == i_fill_band,] df_banded_data_high <- data[data$fillb > i_fill_band,] if (nrow(df_banded_data_high) > 0) { df_banded_data_high$y <- bandwidth df_banded_data_high$fillb <- i_fill_band } df_banded_data_low <- data[data$fillb < i_fill_band,] if (nrow(df_banded_data_low) > 0) { df_banded_data_low$y <- 0 df_banded_data_low$fillb <- i_fill_band } data <- rbind( rbind(df_banded_data, df_banded_data_low), df_banded_data_high ) data$fillb <- data$fillb * bandwidth data$band <- i_fill_band data$group <- i_fill_band data } ) data <- do.call(rbind, banded_data) data$band <- factor(data$band) data } )