... sorry: to be more clear - you would create the biplot with option type="n", 
then use points() and text() to plot whatever you need while using your colour 
vector. 

B.

On Jan 20, 2015, at 3:21 PM, Boris Steipe <boris.ste...@utoronto.ca> wrote:

> Scott -
> 
> A few months ago I posted on this list a modified version of biplot that 
> takes a colour argument (and preserves the axes information, so you can use 
> points() ). I don't have time right now to experiment and look at your code, 
> but perhaps this does "out of the box" what you need.
> 
> Cheers,
> Boris
> 
> ====  Previous post ==========================================================
> Since I've wanted this capability for some time, I modified the original 
> biplot() to accept a type parameter type={"t" (Default) | "p" | "n"}. For 
> "t", the function behaves almost exactly as before. For "p" it plots points, 
> and should accept all the usual arguments for that. For "n" it plots nothing 
> except the axes. You can then add the points as desired.
> 
> I also added two parameters col.arrows = "red", and col.text = "black" to 
> have extra control.
> 
> Here is an example. (Note, you have to load the function, below, first.
> 
> 
> 
> 
> 
> library(MASS)
> data(crabs)
> PRC <- prcomp(crabs[, 4:8])
> 
> myBiplot(PRC)
> myBiplot(PRC, choices=2:3, cex = 0.7, col.text="#445599") # much as before
> 
> 
> # use filled points, color by the value found in column 4 of the data
> r <- range(crabs[,4]) 
> n <- 15
> cv <- cm.colors(n)  
> c <- cv[cut(crabs[,4],n)]  
> myBiplot(PRC, choices=2:3, type = "p", pch=20, col=c, col.arrows = "#FF6600")
> 
> 
> # finally: plot nothing then use points() for detailed control 
> myBiplot(PRC, choices=2:3, type = "n")  # no points
> 
> # blue/orange crab males/females - as given by rows in the data
> points(PRC$x[  1: 50, 2:3], pch=21, bg="#0066FF")
> points(PRC$x[ 51:100, 2:3], pch=24, bg="#0066FF")
> points(PRC$x[101:150, 2:3], pch=21, bg="#FF6600")
> points(PRC$x[151:200, 2:3], pch=24, bg="#FF6600")
> 
> 
> ==============================================================
> myBiplot <- function (x, choices = 1L:2L, scale = 1,
>                     pc.biplot = FALSE, var.axes = TRUE,
>                     type = "t",
>                     col,
>                     col.arrows = "#FF0000",
>                     col.text = "#000000",
>                     cex = rep(par("cex"), 2),
>                     expand = 1, 
>                     xlabs = NULL, ylabs = NULL,
>                     xlim = NULL, ylim = NULL, 
>                     main = NULL, sub = NULL,
>                     xlab = NULL, ylab = NULL, 
>                     arrow.len = 0.1,
>                     ...
>                     )
> 
> {
>       if (length(choices) != 2L) 
>       stop("length of choices must be 2")
>   if (!length(scores <- x$x)) 
>       stop(gettextf("object '%s' has no scores", deparse(substitute(x))), 
>           domain = NA)
>   if (is.complex(scores)) 
>       stop("biplots are not defined for complex PCA")
> 
>   lam <- x$sdev[choices]
>   n <- NROW(scores)
>   lam <- lam * sqrt(n)
> 
>   if (scale < 0 || scale > 1) 
>       warning("'scale' is outside [0, 1]")
>   if (scale != 0) 
>       lam <- lam^scale
>   else lam <- 1
>   if (pc.biplot) 
>       lam <- lam/sqrt(n)
> 
>   y <- t(t(x$rotation[, choices]) * lam)
>   x <- t(t(scores[, choices])/lam)  # note that from here on
>                                     # x is no longer the PC object
>                                     # originally pased into the function
>   n <- nrow(x)
>   p <- nrow(y)
> 
>   if (missing(xlabs)) {
>       xlabs <- dimnames(x)[[1L]]
>       if (is.null(xlabs)) 
>           xlabs <- 1L:n
>   }
>   xlabs <- as.character(xlabs)
>   dimnames(x) <- list(xlabs, dimnames(x)[[2L]])
> 
>   if (missing(ylabs)) {
>       ylabs <- dimnames(y)[[1L]]
>       if (is.null(ylabs)) 
>           ylabs <- paste("Var", 1L:p)
>   }
>   ylabs <- as.character(ylabs)
>   dimnames(y) <- list(ylabs, dimnames(y)[[2L]])
> 
>   if (length(cex) == 1L) 
>       cex <- c(cex, cex)
> 
>   unsigned.range <- function(x) c(-abs(min(x, na.rm = TRUE)), 
>       abs(max(x, na.rm = TRUE)))
>   rangx1 <- unsigned.range(x[, 1L])
>   rangx2 <- unsigned.range(x[, 2L])
>   rangy1 <- unsigned.range(y[, 1L])
>   rangy2 <- unsigned.range(y[, 2L])
> 
>   if (missing(xlim) && missing(ylim)) 
>       xlim <- ylim <- rangx1 <- rangx2 <- range(rangx1, rangx2)
>   else if (missing(xlim)) 
>       xlim <- rangx1
>   else if (missing(ylim)) 
>       ylim <- rangx2
> 
>   ratio <- max(rangy1/rangx1, rangy2/rangx2)/expand
>   on.exit(par(op))
>   op <- par(pty = "s")
>   if (!is.null(main)) 
>       op <- c(op, par(mar = par("mar") + c(0, 0, 1, 0)))
> 
>   # first, plot scores - either normally, or as row labels
>   if (type == "p") {
>           plot(x, type = type, xlim = xlim, ylim = ylim, col = col, 
>       xlab = xlab, ylab = ylab, sub = sub, main = main, ...)
>   }
>   else if (type == "t") {
>       plot(x, type = "n", xlim = xlim, ylim = ylim, 
>            xlab = xlab, ylab = ylab, sub = sub, main = main, ...)
>       text(x, xlabs, cex = cex[1L], col = col.text, ...)
>       
>   }
>   else if (type == "n") {  # plot an empty frame
>           plot(x, type = type, xlim = xlim, ylim = ylim, 
>       xlab = xlab, ylab = ylab, sub = sub, main = main, ...)
>   }
> 
>   par(new = TRUE)
>   dev.hold()
>   on.exit(dev.flush(), add = TRUE)
>   plot(y, axes = FALSE, type = "n", xlim = xlim * ratio, ylim = ylim * 
>       ratio, xlab = "", ylab = "", col = col.arrows, ...)
>   axis(3, col = col.arrows, ...)
>   axis(4, col = col.arrows, ...)
>   box(col = "#000000")
>   text(y, labels = ylabs, cex = cex[2L], col = col.arrows, ...)
>   if (var.axes) 
>       arrows(0, 0, y[, 1L] * 0.8, y[, 2L] * 0.8, col = col.arrows, 
>           length = arrow.len)
>   # now replot into xlim, ylim scaled by lam, to reset par("usr")
>   # to the  correct values needed for subsequent application of points(),
>   # text() etc.
>   par(new = TRUE)
>   dev.hold()
>   on.exit(dev.flush(), add = TRUE)
>       plot(0, type = "n", xlim = xlim * lam[1], ylim = ylim * lam[2], 
>   xlab = '', ylab = '', sub = '', main = '', xaxt='n', yaxt='n', axes=FALSE)
> 
>   invisible()
> 
> }
> 
> ==============================================================
> 
> 
> 
> On Jan 20, 2015, at 2:14 PM, Scott Robinson <scott.robin...@glasgow.ac.uk> 
> wrote:
> 
>> Dear R-Help,
>> 
>> I have been trying to rewrite the base biplot.prcomp function but am coming 
>> across some errors I don't understand. It seems like R is still 'expecting' 
>> the same values despite me rewriting and renaming the methods. My aim is 
>> simply to have an additional biplot function which could use a vector of 
>> colours, where each position of the vector gives the colour for a variable 
>> name and its arrow.
>> 
>> Another issue I have with the default function is that when I save a very 
>> large image (to get good seperation and readability when looking at hundreds 
>> of variables) the names are very displaced from the arrows, but I haven't 
>> even started looking into that yet...
>> 
>> I ran the code on my actual data and got the error:
>>> colouredBiplot(prc, yCol=rep("#FFFF00", 962))
>> Error in if (yCol == NULL) { : argument is of length zero
>>> traceback()
>> 2: colouredBiplot.internal(t(t(scores[, choices])/lam), t(t(x$rotation[,
>>      choices]) * lam), yCol, ...) at colouredBiplot.R#103
>> 1: colouredBiplot(prc, yCol = rep("#FFFF00", 962))
>> 
>> However when I tried creating a small example I got a different error:
>> 
>>> options(stringsAsFactors=F)
>>> source("C:/Work/InGenious/InGen/colouredBiplot.R")
>>> 
>>> random <- matrix(rexp(200, rate=.1), ncol=20)
>>> prc <- prcomp(random, center=T, scale=T)
>>> colouredBiplot(random, rep("#FFFF00", 20))
>> Error in colouredBiplot(random, rep("#FFFF00", 20)) :
>> length of choices must be 2
>>> sessionInfo()
>> R version 3.0.2 (2013-09-25)
>> Platform: x86_64-w64-mingw32/x64 (64-bit)
>> 
>> locale:
>> [1] LC_COLLATE=English_United Kingdom.1252
>> [2] LC_CTYPE=English_United Kingdom.1252
>> [3] LC_MONETARY=English_United Kingdom.1252
>> [4] LC_NUMERIC=C
>> [5] LC_TIME=English_United Kingdom.1252
>> 
>> attached base packages:
>> [1] stats     graphics  grDevices utils     datasets  methods   base
>> 
>> 
>> 
>> 
>> and then immediately after the above I thought to try a traceback() and got 
>> another different error again, which I don't even understand how is possible:
>> 
>>> colouredBiplot(random, yCol=rep("#FFFF00", 20))
>> Error in x$x : $ operator is invalid for atomic vectors
>>> traceback()
>> 1: colouredBiplot(random, yCol = rep("#FFFF00", 20))
>> 
>> 
>> 
>> 
>> The only things I have changed are to pass in a vector "yCol" and use it 
>> inside the else blocks of some conditionals testing 'if(yCol==NULL)':
>> 
>> colouredBiplot.internal <- function (x, y, var.axes = TRUE, col, cex = 
>> rep(par("cex"), 2),
>>   xlabs = NULL, ylabs = NULL, expand = 1, xlim = NULL, ylim = NULL,
>>   arrow.len = 0.1, main = NULL, sub = NULL, xlab = NULL, ylab = NULL, 
>> yCol=NULL,
>>   ...)
>> {
>>   n <- nrow(x)
>>   p <- nrow(y)
>>   if (missing(xlabs)) {
>>       xlabs <- dimnames(x)[[1L]]
>>       if (is.null(xlabs))
>>           xlabs <- 1L:n
>>   }
>>   xlabs <- as.character(xlabs)
>>   dimnames(x) <- list(xlabs, dimnames(x)[[2L]])
>>   if (missing(ylabs)) {
>>       ylabs <- dimnames(y)[[1L]]
>>       if (is.null(ylabs))
>>           ylabs <- paste("Var", 1L:p)
>>   }
>>   ylabs <- as.character(ylabs)
>>   dimnames(y) <- list(ylabs, dimnames(y)[[2L]])
>>   if (length(cex) == 1L)
>>       cex <- c(cex, cex)
>>   if (missing(col)) {
>>       col <- par("col")
>>       if (!is.numeric(col))
>>           col <- match(col, palette(), nomatch = 1L)
>>       col <- c(col, col + 1L)
>>   }
>>   else if (length(col) == 1L)
>>       col <- c(col, col)
>>   unsigned.range <- function(x) c(-abs(min(x, na.rm = TRUE)),
>>       abs(max(x, na.rm = TRUE)))
>>   rangx1 <- unsigned.range(x[, 1L])
>>   rangx2 <- unsigned.range(x[, 2L])
>>   rangy1 <- unsigned.range(y[, 1L])
>>   rangy2 <- unsigned.range(y[, 2L])
>>   if (missing(xlim) && missing(ylim))
>>       xlim <- ylim <- rangx1 <- rangx2 <- range(rangx1, rangx2)
>>   else if (missing(xlim))
>>       xlim <- rangx1
>>   else if (missing(ylim))
>>       ylim <- rangx2
>>   ratio <- max(rangy1/rangx1, rangy2/rangx2)/expand
>>   on.exit(par(op))
>>   op <- par(pty = "s")
>>   if (!is.null(main))
>>       op <- c(op, par(mar = par("mar") + c(0, 0, 1, 0)))
>>   plot(x, type = "n", xlim = xlim, ylim = ylim, col = col[1L],
>>       xlab = xlab, ylab = ylab, sub = sub, main = main, ...)
>>   text(x, xlabs, cex = cex[1L], col = col[1L], ...)
>>   par(new = TRUE)
>>   dev.hold()
>>   on.exit(dev.flush(), add = TRUE)
>>   if(yCol==NULL){
>>   plot(y, axes = FALSE, type = "n", xlim = xlim * ratio, ylim = ylim *
>>       ratio, xlab = "", ylab = "", col = col[1L], ...)
>>       }
>>       else{
>>       plot(y, axes = FALSE, type = "n", xlim = xlim * ratio, ylim = ylim *
>>       ratio, xlab = "", ylab = "", col = yCol, ...)
>>       }
>>   axis(3, col = col[2L], ...)
>>   axis(4, col = col[2L], ...)
>>   box(col = col[1L])
>>   if(yCol==NULL){
>>   text(y, labels = ylabs, cex = cex[2L], col = col[2L], ...)
>>   } else{
>>   text(y, labels = ylabs, cex = cex[2L], col = yCol, ...)
>>   }
>>   if (var.axes){
>>   if(yCol==NULL){
>>       arrows(0, 0, y[, 1L] * 0.8, y[, 2L] * 0.8, col = col[2L],
>>           length = arrow.len)
>>           }else{
>>       arrows(0, 0, y[, 1L] * 0.8, y[, 2L] * 0.8, col = yCol,
>>           length = arrow.len)            }
>>           }
>>   invisible()
>> }
>> 
>> 
>> colouredBiplot <- function (x, choices = 1L:2L, scale = 1, pc.biplot = 
>> FALSE, yCol=NULL, ...)
>> {
>>   if (length(choices) != 2L)
>>       stop("length of choices must be 2")
>>   if (!length(scores <- x$x))
>>       stop(gettextf("object '%s' has no scores", deparse(substitute(x))),
>>           domain = NA)
>>   if (is.complex(scores))
>>       stop("biplots are not defined for complex PCA")
>>   lam <- x$sdev[choices]
>>   n <- NROW(scores)
>>   lam <- lam * sqrt(n)
>>   if (scale < 0 || scale > 1)
>>       warning("'scale' is outside [0, 1]")
>>   if (scale != 0)
>>       lam <- lam^scale
>>   else lam <- 1
>>   if (pc.biplot)
>>       lam <- lam/sqrt(n)
>>   colouredBiplot.internal(t(t(scores[, choices])/lam), t(t(x$rotation[,
>>       choices]) * lam), yCol, ...)
>>   invisible()
>> }
>> 
>> 
>> I have looked into a few alternatives but most either don't allow that type 
>> of different colouring, or else aren't compatable with my various versions 
>> of R.
>> 
>> Help me R-Help, you're my only hope,
>> 
>> Scott
>> 
>> 
>> 
>> 
>>      [[alternative HTML version deleted]]
>> 
>> ______________________________________________
>> R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
>> https://stat.ethz.ch/mailman/listinfo/r-help
>> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
>> and provide commented, minimal, self-contained, reproducible code.
> 
> ______________________________________________
> R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.

______________________________________________
R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.

Reply via email to