Description
The title says it all. The function, which I called ribbon()
is a slight modification to polygon()
with quality of life improvements to make it easier to add colored confidence intervals to a plot. Default values are taken for all polygon arguments, except for border, which is set to NA. Additionally, an alpha argument has been added to accommodate partially transparent confidence intervals. Any of the input values can be overwritten within this function's call.
A standard workflow would look something like this, which could not add in transparency.
data(cars)
# fit model
m1 <- lm(
dist ~ speed,
data = cars
)
# make predictions
preds <- predict(
m1,
newdata = data.frame(speed = 10:25),
interval = "confidence"
)
# base plot
blank(
xlim = c(10,25),
ylim = c(15,120),
xlab = "Speed",
ylab = "Stopping distance",
xaxt = "s",
yaxt = "s",
bty = "l",
las = 1
)
polygon(
x = c(10:25, rev(10:25)),
y = c(preds[,2], rev(preds[,3])),
col = "purple",
border = NA
)
The function I am suggesting would replace polygon and in this case would look like:
ribbon(
x=10:25,
y=preds[,c("lwr","upr")],
col = "purple",
alpha = 0.5
)
In this case x does not need to be reversed (i.e., x = c(10:25, rev(10:25))
) and you can input a two-column data.frame or matrix into y
. Likewise, there is an alpha
argument now which uses base R to set the alpha channel & make colors transparent if you want them to be. Code for this function is:
ribbon <- function(x, y, density=NULL, angle=45, border=NA,
col=NA, lty= par("lty"),...,fillOddEven=FALSE,
alpha = NULL
){
# error checks
if(!any(is.na(col)) & length(col)>1){
warning("Two values input to col. Only first element used.")
col <- col[1]
}
# check if y is a matrix
if(is.matrix(y)|is.data.frame(y)){
y <- c(y[,1], rev(y[,2]))
# check if x is half the length of y
if(length(y)/length(x) == 2){
x <- c(x, rev(x))
}
}
# evaluate color and alpha channel
if( is.na(col) ){
my_col <- NA
} else { # otherwise go through color process
#
if(
length(grep("^#", col)) == 1 & # if start with hash
nchar(col)>7 # & alpha channel is present
){
if(!is.null(alpha)){
warning("col already has alpha channel, ignoring alpha argument.")
my_col <- col
} else {
my_col <- col
}
} else {
# get rgb
my_rgbs <- col2rgb(col)
# set color
my_col <- rgb(
my_rgbs[1],my_rgbs[2],my_rgbs[3],max = 255,alpha = 255 * alpha
)
}
}
polygon(
x = x, y = y, density = density, angle = angle,
border = border, col = my_col, lty = lty,
fillOddEven = fillOddEven, ...
)
}
Code, documentation, and tests are written. Happy to submit a PR if you want to check it out!