Friday, January 18, 2013

Choosing colors visually with 'getcolors'


When plotting, I am constantly defaulting to the "main" colors in R - In other words, the colors that one can quickly call by number (1="black", 2="red", 3="green", 4="blue", ... etc.) . In my opinion, these colors do not lend themselves well to compelling graphics. I imagine this is the reason for the inclusion of the much more pleasing color palettes used by default in the popular graphical package ggplot2. I try and choose better colors for final figure versions for publishing, but it is usually a tedious process of trial and error with functions like rgb(). There are some nice alternate color palettes out there probably more in line with color theory, and one has a lot of flexibility with functions like colorRampPalette(), but I wanted to have a function where I can choose colors visually in order to speed up the process. Below is the function getcolors(), which allows for this selection by using a simplified color swatch to allow selection with a mouse using the locator() function (above, top plot). Following selection, a second plot opens showing how these colors look next to each other and on a background gradient of black to white. The function uses an RGB color model: Red increases on the y-axis, Green increases on the x-axis, and Blue is a repeated sequence of levels across the x-axis).

For the example, I chose 4 colors, which are saved in a vector. These colors were subsequently used to make the following line plot:


the getcolors function:

getcolors <- function(n){
 N <- 6
 
 X <- seq(N^2)
 Y <- seq(N)
 GRD <- expand.grid(x=X, y=Y)
 Z <- matrix(0, nrow=length(X), ncol=length(Y))
 
 LEV <- seq(0,1,,N) 
 R <- matrix(rep(LEV, each=N^2), nrow=length(X), ncol=length(Y))
 G <- matrix(rep(rep(LEV, each=N), N), nrow=length(X), ncol=length(Y))
 B <- matrix(rep(LEV, N^2), nrow=length(X), ncol=length(Y))
 
 
 x11(width=6, height=6)
 layout(matrix(1:2, nrow=2, ncol=1), widths=c(6), heights=c(1.5,4.5))
 op <- par(mar=c(1,3,2,1))
 
 image(X,Y,Z, col=NA, xlab="", ylab="", xaxt="n", yaxt="n")
 for(i in seq(nrow(GRD))){
  xs <- c(GRD$x[i]-0.5, GRD$x[i]-0.5, GRD$x[i]+0.5, GRD$x[i]+0.5)
  ys <- c(GRD$y[i]-0.5, GRD$y[i]+0.5, GRD$y[i]+0.5, GRD$y[i]-0.5)
  polygon(xs, ys, col=rgb(R[i], G[i], B[i]), border=NA)
 }
 mtext(paste("Click on", n, "colors [please]"), side=3, line=0.5)
 box()
 
 COLS <- NA*seq(n)
 for(i in seq(n)){
  coord <- locator(1)
  COLS[i] <- rgb(R[round(coord$x), round(coord$y)], G[round(coord$x), round(coord$y)], B[round(coord$x), round(coord$y)])
 }
 
 par(mar=c(1,3,0,1))
 pal <- colorRampPalette(c("black", "white"))
 image(x=1:100, y=seq(n), z=matrix(rep(1:100,n), nrow=100, ncol=n), col=pal(100), xlab="", ylab="", xaxt="n", yaxt="n")
 box()
 for(i in seq(n)){
  lines(x=c(1,100), y=c(i,i), col=COLS[i], lwd=4)
 }
 axis(2, at=seq(n))
 
 par(op)
 
 COLS
}
Created by Pretty R at inside-R.org


to reproduce the example:
source("getcolors.R")
 
set.seed(1111)
n <- 100
x <- seq(n)
y1 <- cumsum(rnorm(n))
y2 <- cumsum(rnorm(n))
y3 <- cumsum(rnorm(n))
y4 <- cumsum(rnorm(n))
 
ylim <- range(c(y1,y2,y3,y4))
 
COLS <- getcolors(4)
 
x11()
plot(x, y1, ylim=ylim, t="l", col=COLS[1], lwd=3, ylab="")
lines(x, y2, col=COLS[2], lwd=3)
lines(x, y3, col=COLS[3], lwd=3)
lines(x, y4, col=COLS[4], lwd=3)
legend("topleft", legend=paste("y", 1:4, sep=""), col=COLS, lwd=3)
Created by Pretty R at inside-R.org






2 comments:

  1. Have you thought about using HSL or LAB color spaces?

    ReplyDelete
  2. I thought about using hsl, but only in terms of trying to get a good swatch of colors presented in a straightforward way. I didn't come up with a better solution than the set of matrices above. If you have a better suggestion, I would love to hear it. I actually wanted to do something like a triplot with the rgb levels, but the only plot I could find, was from the plotrix package and it seems to be limited to percentages - I'm sure there is a better way of doing this...

    ReplyDelete