hugovdberg
hugovdberg

Reputation: 1631

Is it possible to expand geom_ribbon to xlimits?

I have the following code (as an example) which I would like to adapt such that the ribbon extends to the entire xrange, as geom_hline() does. The ribbon indicates what values are within accepted bounds. In my real application sometimes has no upper or lower bound, so the hline by itself is not enough to determine whether values are within bounds.

library(ggplot2)
set.seed(2016-12-19)
dates <- seq(as.Date('2016-01-01'), as.Date('2016-12-31'), by = 1)
values <- rexp(length(dates), 1)
groups <- rpois(length(dates), 5)
temp <- data.frame(
    date = dates,
    value = values,
    group = groups,
    value.min = 0,
    value.max = 2
)
ggplot(temp, aes(date, value)) +
    geom_ribbon(aes(ymin = value.min, ymax = value.max), fill = '#00cc33', alpha = 0.6) +
    geom_hline(aes(yintercept = value.min)) +
    geom_hline(aes(yintercept = value.max)) +
    geom_point() +
    facet_wrap(~group)

I tried setting the x in geom_ribbon to datesas well, but then only fractions of the range are filled. Also I tried this:

geom_ribbon(aes(ymin = -Inf, ymax = 2, x = dates), data = data.frame(), fill = '#00cc33', alpha = 0.6)

but then the data seems to be overwritten for the entire plot and I get the error Error in eval(expr, envir, enclos) : object 'value' not found. Even if it would work then the range is still actually too narrow as the xlimits are expanded.

Upvotes: 4

Views: 1669

Answers (2)

hugovdberg
hugovdberg

Reputation: 1631

Based on lukeA's answer I produced the following code, which I think is a little simpler:

library(ggplot2)
set.seed(2016-12-19)
dates <- seq(as.Date('2016-01-01'), as.Date('2016-12-31'), by = 1)
values <- rexp(length(dates), 1)
groups <- rpois(length(dates), 5)
temp <- data.frame(
    date = dates,
    value = values,
    group = groups,
    value.min = 1,
    value.max = 2
)
bounds <- data.frame(
    xmin = -Inf,
    xmax = Inf,
    ymin = temp$value.min[1],
    ymax = temp$value.max[1]
)
ggplot(temp, aes(date, value)) +
    geom_rect(
        aes(
            xmin = as.Date(xmin, origin = '1970-01-01'),
            xmax = as.Date(xmax, origin = '1970-01-01'),
            ymin = ymin,
            ymax = ymax
        ),
        data = bounds,
        fill = '#00cc33',
        alpha = 0.3,
        inherit.aes = FALSE
    ) +
    geom_point() +
    facet_wrap(~group)

I created a temporary dataframe containing the bounds for the rectangle, and added inherit.aes = FALSE since apparently the bounds otherwise overrule the temp data.frame (still seems a bug to me). By transforming the -Inf and Inf to the correct datatype I didn't need the custom labeler (if your dealing with POSIXt use the correct as.POSIXct/lt as automatic transformation fails).

Upvotes: 1

lukeA
lukeA

Reputation: 54237

Here's one way to do it:

ggplot(temp, aes(as.numeric(date), value)) +
  geom_rect(aes(xmin=-Inf, xmax=Inf, ymin = value.min, ymax = value.max), temp[!duplicated(temp$group),], fill = '#00cc33', alpha = 0.6) + 
  geom_hline(aes(yintercept = value.min)) +
  geom_hline(aes(yintercept = value.max)) +
  geom_point() +
  scale_x_continuous(labels = function(x) format(as.Date(x, origin = "1970-01-01"), "%b %y")) + 
  facet_wrap(~group)

enter image description here

Note that I used as.numeric(date), because otherwise Inf and -Inf yield

Error: Invalid input: date_trans works with objects of class Date only

To get date labels for numeric values, I adjusted the scale_x_continuous labels accordingly. (Although they are not exact here. You may want to adjust it by using the exact dates instead of month/year, or alternatively set manual breaks using the breaks argument and for example seq.Date.)

Also note that I used temp[!duplicated(temp$group),] to avoid overplotting and thus maintaining the desired alpha transparency.

Upvotes: 3

Related Questions