diff --git a/R/corrplot.R b/R/corrplot.R index 723cd96..557e09e 100644 --- a/R/corrplot.R +++ b/R/corrplot.R @@ -27,9 +27,19 @@ #' otherwise a new plot is created. #' #' @param col Vector, the color of glyphs. It is distributed uniformly in -#' \code{cl.lim}. If NULL, \code{col} will be +#' \code{cl.lim} interval. If NULL, \code{col} will be #' \code{colorRampPalette(col2)(200)}, see example about col2. #' +#' @param cl.lim The limits \code{(x1, x2)} interval for assigning color by +#' \code{col}. If \code{NULL}, +#' \code{cl.lim} will be \code{c(-1, 1)} when \code{is.corr} is \code{TRUE}, . +#' \code{cl.lim} will be \code{c(min(corr), max(corr))} when \code{is.corr} +#' is \code{FALSE} +#' +#' NOTICE: if you set \code{cl.lim} when \code{is.corr TRUE}, the assigning color +#' method is still distributed uniformly in [-1, 1], it only affect the display +#' on colorlegend. +#' #' #' @param bg The background color. #' @@ -107,8 +117,6 @@ #' \code{"full"}), \code{"b"} (default if \code{type=="lower"}) or \code{"n"}, #' \code{"n"} means don't draw colorlabel. #' -#' @param cl.lim The limits \code{(x1, x2)} in the colorlabel. -#' #' @param cl.length Integer, the number of number-text in colorlabel, passed to #' \code{\link{colorlegend}}. If \code{NULL}, \code{cl.length} is #' \code{length(col) + 1} when \code{length(col) <=20}; \code{cl.length} is 11 @@ -241,7 +249,7 @@ corrplot <- function(corr, method = c("circle", "square", "ellipse", "number", "shade", "color", "pie"), type = c("full", "lower", "upper"), add = FALSE, - col = NULL, bg = "white", title = "", is.corr = TRUE, + col = NULL, cl.lim = NULL, bg = "white", title = "", is.corr = TRUE, diag = TRUE, outline = FALSE, mar = c(0, 0, 0, 0), addgrid.col = NULL, addCoef.col = NULL, addCoefasPercent = FALSE, @@ -253,9 +261,8 @@ corrplot <- function(corr, tl.pos = NULL, tl.cex = 1, tl.col = "red", tl.offset = 0.4, tl.srt = 90, - cl.pos = NULL, cl.lim = NULL, - cl.length = NULL, cl.cex = 0.8, cl.ratio = 0.15, - cl.align.text = "c", cl.offset = 0.5, + cl.pos = NULL, cl.length = NULL, cl.cex = 0.8, + cl.ratio = 0.15, cl.align.text = "c", cl.offset = 0.5, number.cex = 1, number.font = 2, number.digits = NULL, @@ -282,6 +289,8 @@ corrplot <- function(corr, insig <- match.arg(insig) plotCI <- match.arg(plotCI) + + # rescale symbols within the corrplot based on win.asp parameter if (win.asp != 1 && !(method %in% c("circle", "square"))) { stop("Parameter 'win.asp' is supported only for circle and square methods.") @@ -304,25 +313,45 @@ corrplot <- function(corr, stop("color limits should cover matrix") } + if (is.null(cl.lim)) { if (is.corr) { # if the matrix is expected to be a correlation matrix # it MUST be within the interval [-1,1] - cl.lim <- c(-1,1) + cl.lim <- c(-1, 1) } else { # Issue #91 # if not a correlation matrix and the diagonal is hidden, # we need to compute limits from all cells except the diagonal - corr_tmp <- corr - diag(corr_tmp) <- ifelse( - rep(diag, length(diag(corr_tmp))), - diag(corr_tmp), - NA - ) - cl.lim <- c(min(corr_tmp, na.rm = TRUE), max(corr_tmp, na.rm = TRUE)) + + if(!diag) { + diag(corr) = NA + } + + cl.lim <- c(min(corr, na.rm = TRUE), max(corr, na.rm = TRUE)) + } + } + + # if the mat have both negative and positive values, it is a SpecialCorr + SpecialCorr = 0 + + if(is.corr) { + # check the interval if expecting a correlation matrix + # otherwise, the values can be any number + if (min(corr, na.rm = TRUE) < -1 - .Machine$double.eps ^ .75 || + max(corr, na.rm = TRUE) > 1 + .Machine$double.eps ^ .75 ) { + stop("The matrix is not in [-1, 1]!") + } + + + SpecialCorr = 1 + + if(cl.lim[1] < -1 | cl.lim[2] > 1) { + stop('cl.lim should be within the interval [-1,1]') } } + intercept <- 0 zoom <- 1 @@ -331,46 +360,41 @@ corrplot <- function(corr, c_max <- max(corr, na.rm = TRUE) c_min <- min(corr, na.rm = TRUE) - # The following if-elseif-else code should exhaustively cover all 9 - # combinations of c_min and c_max variables. Each variable can be either - # zero (0), positive (+) or negative (-). - - # c_min c_max - - # 00 - # -0 - # +0 - # -- - # 0- - if (c_max <= 0) { - intercept <- -cl.lim[2] - zoom <- 1 / (diff(cl.lim)) + if(diff(cl.lim)/(c_max - c_min)> 2) { + warning("cl.lim interval too wide, please set a suitable value") } - # ++ - # +- - # 0+ - else if (c_min >= 0) { - intercept <- -cl.lim[1] + # all negative or positive, trans to [0, 1] + if (c_max <= 0 | c_min>=0) { + intercept <- -c_min zoom <- 1 / (diff(cl.lim)) + + + if(cl.lim[1] * cl.lim[2] < 0) { + warning("cl.lim interval not suitable to the matrix") + } + } - # -+ + + # mixed negative and positive, remain its sign, e.g. [-0.8, 1] or [-1, 0.7] else { # expression from the original code as a sanity check stopifnot(c_max * c_min < 0) - - # newly derived expression which covers the single remainig case + # newly derived expression which covers the single remaining case stopifnot(c_min < 0 && c_max > 0) + + intercept <- 0 zoom <- 1 / max(abs(cl.lim)) + SpecialCorr <- 1 } - # now, the zoom might still be Inf when cl.lim were both zero + # now, the zoom might still be Inf when c_max and c_min were both zero if (zoom == Inf) { - stopifnot(cl.lim[1] == 0 && cl.lim[2] == 0) # check the assumption + stopifnot(c_max == 0 && c_min == 0) # check the assumption zoom <- 0 } @@ -380,14 +404,7 @@ corrplot <- function(corr, cl.lim2 <- (intercept + cl.lim) * zoom int <- intercept * zoom - if (is.corr) { - # check the interval if expecting a correlation matrix - # otherwise, the values can be any number - if (min(corr, na.rm = TRUE) < -1 - .Machine$double.eps ^ .75 || - max(corr, na.rm = TRUE) > 1 + .Machine$double.eps ^ .75 ) { - stop("The matrix is not in [-1, 1]!") - } - } + if (is.null(col)) { col <- colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", @@ -494,8 +511,14 @@ corrplot <- function(corr, ## assign colors - assign.color <- function(dat = DAT, color = col) { - newcorr <- (dat + 1) / 2 + assign.color <- function(dat = DAT, color = col, isSpecialCorr = SpecialCorr) { + + if(isSpecialCorr) { + newcorr <- (dat + 1) / 2 + } else { + newcorr <- dat + } + newcorr[newcorr <= 0] <- 0 newcorr[newcorr >= 1] <- 1 - 1e-16 color[floor(newcorr * length(color)) + 1] # new color returned @@ -889,6 +912,8 @@ corrplot <- function(corr, ### color legend if (cl.pos != "n") { colRange <- assign.color(dat = cl.lim2) + + ind1 <- which(col == colRange[1]) ind2 <- which(col == colRange[2]) colbar <- col[ind1:ind2] diff --git a/man/corrplot.Rd b/man/corrplot.Rd index a7bff1b..26d3e66 100644 --- a/man/corrplot.Rd +++ b/man/corrplot.Rd @@ -10,6 +10,7 @@ corrplot( type = c("full", "lower", "upper"), add = FALSE, col = NULL, + cl.lim = NULL, bg = "white", title = "", is.corr = TRUE, @@ -31,7 +32,6 @@ corrplot( tl.offset = 0.4, tl.srt = 90, cl.pos = NULL, - cl.lim = NULL, cl.length = NULL, cl.cex = 0.8, cl.ratio = 0.15, @@ -82,9 +82,19 @@ matrix.} otherwise a new plot is created.} \item{col}{Vector, the color of glyphs. It is distributed uniformly in -\code{cl.lim}. If NULL, \code{col} will be +\code{cl.lim} interval. If NULL, \code{col} will be \code{colorRampPalette(col2)(200)}, see example about col2.} +\item{cl.lim}{The limits \code{(x1, x2)} interval for assigning color by + \code{col}. If \code{NULL}, + \code{cl.lim} will be \code{c(-1, 1)} when \code{is.corr} is \code{TRUE}, . + \code{cl.lim} will be \code{c(min(corr), max(corr))} when \code{is.corr} + is \code{FALSE} + + NOTICE: if you set \code{cl.lim} when \code{is.corr TRUE}, the assigning color + method is still distributed uniformly in [-1, 1], it only affect the display + on colorlegend.} + \item{bg}{The background color.} \item{title}{Character, title of the graph.} @@ -161,8 +171,6 @@ it must be one of \code{"r"} (default if \code{type=="upper"} or \code{"full"}), \code{"b"} (default if \code{type=="lower"}) or \code{"n"}, \code{"n"} means don't draw colorlabel.} -\item{cl.lim}{The limits \code{(x1, x2)} in the colorlabel.} - \item{cl.length}{Integer, the number of number-text in colorlabel, passed to \code{\link{colorlegend}}. If \code{NULL}, \code{cl.length} is \code{length(col) + 1} when \code{length(col) <=20}; \code{cl.length} is 11 @@ -284,6 +292,7 @@ M <- cor(mtcars) set.seed(0) ## different color series +col0 <- colorRampPalette(c("white", "cyan", "#007FFF", "blue","#00007F")) col1 <- colorRampPalette(c("#7F0000", "red", "#FF7F00", "yellow", "white", "cyan", "#007FFF", "blue","#00007F")) col2 <- colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", @@ -292,6 +301,8 @@ col2 <- colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", col3 <- colorRampPalette(c("red", "white", "blue")) col4 <- colorRampPalette(c("#7F0000", "red", "#FF7F00", "yellow", "#7FFF7F", "cyan", "#007FFF", "blue", "#00007F")) + + wb <- c("white", "black") par(ask = TRUE) @@ -358,12 +369,26 @@ corrplot(M, order = "hclust", addrect = 3, rect.col = "red") corrplot(M, order = "hclust", addrect = 4, rect.col = "blue") corrplot(M, order = "hclust", hclust.method = "ward.D2", addrect = 4) - - ## visualize a matrix in [0, 1] corrplot(abs(M), order = "AOE", cl.lim = c(0,1)) corrplot(abs(M), order = "AOE", col = col1(20), cl.lim = c(0,1)) -corrplot(abs(M), order = "AOE", col = col3(200), cl.lim = c(0,1)) + + +# when is.corr=TRUE, cl.lim only affect the color legend +# If you change it, the color is still assigned on [-1, 1] +corrplot(M/2) +corrplot(M/2, cl.lim=c(-0.5,0.5)) + +# when is.corr=FALSE, cl.lim is also used to assign colors +# if the matrix have both positive and negative values +# the matrix transformation keep every values positive and negative +corrplot(M*2, is.corr = FALSE, cl.lim=c(-2, 2)) +corrplot(M*2, is.corr = FALSE, cl.lim=c(-2, 2) * 2) +corrplot(M*2, is.corr = FALSE, cl.lim=c(-2, 2) * 4) + +## 0.5~0.6 +corrplot(abs(M)/10+0.5, col=col0(10)) +corrplot(abs(M)/10+0.5, is.corr = FALSE, cl.lim=c(0.5,0.6), col=col0(10)) ## visualize a matrix in [-100, 100] @@ -371,6 +396,15 @@ ran <- round(matrix(runif(225, -100,100), 15)) corrplot(ran, is.corr = FALSE) corrplot(ran, is.corr = FALSE, cl.lim = c(-100, 100)) +## visualize a matrix in [100, 300] +ran2 <- ran + 200 + +# bad color +corrplot(ran2, is.corr = FALSE, cl.lim = c(100, 300), col=col1(100)) + +# good color +corrplot(ran2, is.corr = FALSE, cl.lim = c(100, 300), col=col0(100)) + ## text-labels and plot type corrplot(M, order = "AOE", tl.srt = 45) diff --git a/tests/testthat/test-corrplot.R b/tests/testthat/test-corrplot.R index 9d45129..4c95f1d 100644 --- a/tests/testthat/test-corrplot.R +++ b/tests/testthat/test-corrplot.R @@ -67,11 +67,7 @@ test_that("Issue #7: Enable to plot a matrix with NA", { expect_equal(corrplot(M), M) }) -test_that("Issue #70: Enable to plot a matrix with NA when 'is.corr = FALSE'", { - M <- matrix(0, ncol = 5, nrow = 5) - M[1,1] <- NA - expect_true(is.matrix(corrplot(M, is.corr = FALSE))) -}) + test_that("Issue #20: plotmath expressions in rownames / colnames", { M <- cor(mtcars)[1:5,1:5] diff --git a/vignettes/corrplot-intro.Rmd b/vignettes/corrplot-intro.Rmd index 9be47f6..bfc5cad 100644 --- a/vignettes/corrplot-intro.Rmd +++ b/vignettes/corrplot-intro.Rmd @@ -26,7 +26,22 @@ knitr::opts_chunk$set( fig.height = 6, dev = "png") -if(all(capabilities(c('cairo', 'X11')))){ +get_os <- function(){ + sysinf <- Sys.info() + if (!is.null(sysinf)){ + os <- sysinf['sysname'] + if (os == 'Darwin') + os <- "osx" + } else { ## mystery machine + os <- .Platform$OS.type + if (grepl("^darwin", R.version$os)) + os <- "osx" + if (grepl("linux-gnu", R.version$os)) + os <- "linux" + } + tolower(os) +} +if(get_os() =='windows' & capabilities('cairo')){ knitr::opts_chunk$set(dev.args = list(type="cairo")) } ``` @@ -68,15 +83,13 @@ There are three layout types (parameter `type`): - `"lower"` : display lower triangular of the **correlation matrix** ```{r layout} -corrplot(M, type = "upper") -corrplot(M, type = "upper") +corrplot(M, type = "lower") ``` `corrplot.mixed()` is a wrapped function for mixed visualization style. ```{r mixed} corrplot.mixed(M) corrplot.mixed(M, lower.col = "black", number.cex = .7) -corrplot.mixed(M, lower = "ellipse", upper = "circle") corrplot.mixed(M, lower = "square", upper = "circle", tl.col = "black") ``` @@ -121,18 +134,16 @@ You can also reorder the matrix "manually" via function `corrMatOrder()`. corrplot(M, order = "AOE") corrplot(M, order = "hclust") corrplot(M, order = "FPC") -corrplot(M, order = "alphabet") ``` If using `"hclust"`, `corrplot()` can draw rectangles around the chart of corrrlation matrix based on the results of hierarchical clustering. ```{r rectangles} -corrplot(M, order = "hclust", addrect = 2) corrplot(M, order = "hclust", addrect = 3) ``` ```{r hclust-lightblue} # Change background color to lightblue -corrplot(M, type = "upper", order = "hclust", +corrplot(M, type = "upper", order = "hclust", cl.pos = "n", col = c("black", "white"), bg = "lightblue") ``` @@ -145,6 +156,10 @@ The function `colorRampPalette()` is very convenient for generating color spectrum. ```{r color} +## color example, suitable for matrix in [-N, 0] or [0, N] +col0 <- colorRampPalette(c("white", "cyan", "#007FFF", "blue","#00007F")) + +## diverging color example, suitable for matrix in [-N, N] col1 <- colorRampPalette(c("#7F0000", "red", "#FF7F00", "yellow", "white", "cyan", "#007FFF", "blue", "#00007F")) col2 <- colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", @@ -165,10 +180,7 @@ corrplot(M, order = "hclust", addrect = 2, col = whiteblack, bg = "gold2") You can also use the standard color palettes (package `grDevices`) ```{r hclust-stdcolors} -corrplot(M, order = "hclust", addrect = 2, col = heat.colors(100)) -corrplot(M, order = "hclust", addrect = 2, col = terrain.colors(100)) corrplot(M, order = "hclust", addrect = 2, col = cm.colors(100)) -corrplot(M, order = "hclust", addrect = 2, col = gray.colors(100)) ``` Other option would be to use `RcolorBrewer` package. @@ -177,10 +189,6 @@ library(RColorBrewer) corrplot(M, type = "upper", order = "hclust", col = brewer.pal(n = 8, name = "RdBu")) -corrplot(M, type = "upper", order = "hclust", - col = brewer.pal(n = 8, name = "RdYlBu")) -corrplot(M, type = "upper", order = "hclust", - col = brewer.pal(n = 8, name = "PuOr")) ``` @@ -208,16 +216,28 @@ corrplot(M, type = "lower", order = "hclust", tl.col = "black", tl.srt = 45) ``` -# Dealing with a non-correlation matrix +# Assign and display color using cl.lim + +```{r cl_lim} +# when is.corr=TRUE, cl.lim only affect the color legend +# If you change it, the color is still assigned on [-1, 1] +corrplot(M/2) +corrplot(M/2, cl.lim=c(-0.5,0.5)) + +# when is.corr=FALSE, cl.lim is also used to assign colors +# if the matrix have both positive and negative values +# the matrix transformation keep every values positive and negative +corrplot(M*2, is.corr = FALSE, cl.lim=c(-2, 2)) +``` +# Dealing with a non-correlation matrix ```{r non-corr} -corrplot(abs(M),order = "AOE", col = col3(200), cl.lim = c(0, 1)) -## visualize a matrix in [-100, 100] -ran <- round(matrix(runif(225, -100,100), 15)) -corrplot(ran, is.corr = FALSE, method = "square") -## a beautiful color legend -corrplot(ran, is.corr = FALSE, method = "ellipse", cl.lim = c(-100, 100)) +# for showing the usage of cl.lim, run with warning +corrplot(M*2, is.corr = FALSE, cl.lim=c(-2, 2) * 2) + +## matrix in 0.5~0.6, a good example +corrplot(abs(M)*10+50, is.corr = FALSE, cl.lim=c(50, 60), col=col0(10)) ``` If your matrix is rectangular, you can adjust the aspect ratio with the @@ -309,18 +329,3 @@ corrplot(M, p.mat = res1$p, method = "color", type = "upper", corrplot(M, p.mat = res1$p, insig = "label_sig", pch.col = "white", pch = "p<.05", pch.cex = .5, order = "AOE") ``` - - - -# Explore Large Correlation Matrices - - -```{r large_matrix} -n <- 100 -m <- 10 -DATASET <- matrix(runif(n * m), nrow = m, ncol = n) - -# A 100x100 correlation matrix, tl.cex = 0.45 -corrplot(cor(DATASET), method = "color", type = "upper", - order = "FPC", tl.pos = "td", tl.cex = 0.45) -``` diff --git a/vignettes/example-corrplot.R b/vignettes/example-corrplot.R index ae150a9..47090bd 100644 --- a/vignettes/example-corrplot.R +++ b/vignettes/example-corrplot.R @@ -3,6 +3,7 @@ M <- cor(mtcars) set.seed(0) ## different color series +col0 <- colorRampPalette(c("white", "cyan", "#007FFF", "blue","#00007F")) col1 <- colorRampPalette(c("#7F0000", "red", "#FF7F00", "yellow", "white", "cyan", "#007FFF", "blue","#00007F")) col2 <- colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", @@ -11,6 +12,8 @@ col2 <- colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", col3 <- colorRampPalette(c("red", "white", "blue")) col4 <- colorRampPalette(c("#7F0000", "red", "#FF7F00", "yellow", "#7FFF7F", "cyan", "#007FFF", "blue", "#00007F")) + + wb <- c("white", "black") par(ask = TRUE) @@ -77,12 +80,26 @@ corrplot(M, order = "hclust", addrect = 3, rect.col = "red") corrplot(M, order = "hclust", addrect = 4, rect.col = "blue") corrplot(M, order = "hclust", hclust.method = "ward.D2", addrect = 4) - - ## visualize a matrix in [0, 1] corrplot(abs(M), order = "AOE", cl.lim = c(0,1)) corrplot(abs(M), order = "AOE", col = col1(20), cl.lim = c(0,1)) -corrplot(abs(M), order = "AOE", col = col3(200), cl.lim = c(0,1)) + + +# when is.corr=TRUE, cl.lim only affect the color legend +# If you change it, the color is still assigned on [-1, 1] +corrplot(M/2) +corrplot(M/2, cl.lim=c(-0.5,0.5)) + +# when is.corr=FALSE, cl.lim is also used to assign colors +# if the matrix have both positive and negative values +# the matrix transformation keep every values positive and negative +corrplot(M*2, is.corr = FALSE, cl.lim=c(-2, 2)) +corrplot(M*2, is.corr = FALSE, cl.lim=c(-2, 2) * 2) +corrplot(M*2, is.corr = FALSE, cl.lim=c(-2, 2) * 4) + +## 0.5~0.6 +corrplot(abs(M)/10+0.5, col=col0(10)) +corrplot(abs(M)/10+0.5, is.corr = FALSE, cl.lim=c(0.5,0.6), col=col0(10)) ## visualize a matrix in [-100, 100] @@ -90,6 +107,15 @@ ran <- round(matrix(runif(225, -100,100), 15)) corrplot(ran, is.corr = FALSE) corrplot(ran, is.corr = FALSE, cl.lim = c(-100, 100)) +## visualize a matrix in [100, 300] +ran2 <- ran + 200 + +# bad color +corrplot(ran2, is.corr = FALSE, cl.lim = c(100, 300), col=col1(100)) + +# good color +corrplot(ran2, is.corr = FALSE, cl.lim = c(100, 300), col=col0(100)) + ## text-labels and plot type corrplot(M, order = "AOE", tl.srt = 45)