I had to dive deep into the github repo, but I finally got it. To do this, you need to know how stat_smooth works. In this particular case, the loess function is called to perform smoothing (various smoothing functions can be built using the same process as below):
So, using loess in this case, we will do:
#data df <- mtcars[,c("mpg","cyl"), with=FALSE] #run loess model cars.lo <- loess(cyl ~ mpg, df)
Then I had to read this to see how forecasts were made inside stat_smooth . Hasley apparently uses the predictdf function (which is not exported to the namespace), as described below for our case:
predictdf.loess <- function(model, xseq, se, level) { pred <- stats::predict(model, newdata = data.frame(x = xseq), se = se) if (se) { y = pred$fit ci <- pred$se.fit * stats::qt(level / 2 + .5, pred$df) ymin = y - ci ymax = y + ci data.frame(x = xseq, y, ymin, ymax, se = pred$se.fit) } else { data.frame(x = xseq, y = as.vector(pred)) } }
After reading the above, I was able to create my own data.frame of forecasts using:
Looking at predictdf.loess , we see that the upper bound of the confidence interval is created as pred$fit + pred$se.fit * qt(0.95 / 2 + .5, pred$df) , and the lower bound is created as pred$fit - pred$se.fit * qt(0.95 / 2 + .5, pred$df) .
Using them, we can create a flag for points above or below these borders:
#make the flag outerpoints <- +(df$cyl > df2$fit + df2$se.fit | df$cyl < df2$fit - df2$se.fit)
The df$outer column is probably looking for OP (it takes the value 1 if it is outside the bounds or 0 otherwise), but just for the sake of it I draw it below.
Note that the + function above is used here only to convert a boolean flag to a numeric value.
Now, if we build it like:
ggplot(df,aes(mpg,cyl)) + geom_point(aes(colour=factor(outer))) + geom_smooth()
We can see the points inside and outside the confidence interval.
Output:

PS For those interested in the upper and lower bounds, they are created in this way (assumption: although the shaded areas are probably created using geom_ribbon - or something like that - which makes them more round and beautiful):
#upper boundary ggplot(df,aes(mpg,cyl)) + geom_point(aes(colour=factor(outer))) + geom_smooth() + geom_line(data=df2, aes(mpg , fit + se.fit , group=1), colour='red')