Let’s say you have some data like this

1
2
3
4
5
set.seed(42)
data <- data.frame(
  x = seq(1,30),
  y = 0.3 * seq(1,30) + 2 * rnorm(30) 
)

So let’s plot it using ggplot:

1
2
library(ggplot2)
ggplot(data, aes(x=x, y=y)) + geom_point() + geom_line()

Plotting an aim-corridor

If this is something like your revenue, pageviews or something like that you may ask: “Is this good or is this bad?”

So chances are good you have an aim for this data like this one here and merge it with your data

1
2
3
4
5
6
7
corridor <- data.frame(
  x    = data$x,
  ymin = 0.3 * data$x - 1,
  ymax = 0.3 * data$x + 1
)
 
all.data <- merge(data, corridor, by.x='x')

Now we can use ggplot’s geom_ribbon to plot this corridor:

1
2
3
ggplot(all.data, aes(x=x, y=y)) + 
  geom_ribbon(aes(x=x, ymin=ymin, ymax=ymax), fill="blue", alpha=0.1) + 
  geom_point() + geom_line()

The result looks better. But there is even an enhancement possible.

Fully colour-coding the area above and below the corridor

But now the question is if it’s better to be above or below the corridor? Think about cost per customer. Than below the corridor is better. But if the data is revenue per customer above the corridor would be better.

So let’s colour-code the areas below and above the corridor.

Therefor we need to now the boundaries of the actual plot. We use the function ggplot_build to get the meta-info of the plot and plot it again:

I’ve updated this snippet because ggplot2 2.2.x now uses panel_ranges instead of ranges.

I’ve updated this snippet again because ggplot2 3.3.x now uses panel_params instead of panel_ranges.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
g <- ggplot(all.data, aes(x=x, y=y)) + 
  geom_ribbon(aes(x=x, ymin=ymin, ymax=ymax), fill="blue", alpha=0.1) + 
  geom_point() + geom_line() 
 
 
res <- ggplot_build(g)
 
bottom <- res[[2]]$panel_params[[1]]$y.range[1]
top    <- res[[2]]$panel_params[[1]]$y.range[2]
 
ggplot(all.data, aes(x=x, y=y)) + 
  geom_ribbon(aes(x=x, ymin=ymin, ymax=ymax), fill="blue", alpha=0.1) +
  geom_ribbon(aes(x=x, ymin=bottom, ymax=ymin), fill="red", alpha=0.1) + 
  geom_ribbon(aes(x=x, ymin=ymax, ymax=top), fill="green", alpha=0.1) + 
  geom_point() + geom_line()

Next steps

I would like encapsulate the whole process of generating the corridor and the areas above and below into a new geom. So I would like to write an extension to ggplot as mentioned in the documentation of ggplot2. But I haven’t done that yet.