#' @importFrom grDevices colorRampPalette
#' @importFrom graphics image points
#' @importFrom utils head
NULL
#' Plots a 2D Density
#'
#' This function tries to replicate 'smart' 2D density plots
#' as they are found in flow cytometry data analysis software.
#' These plots commonly work with millions of points. In dense
#' regions, they will plot these as a heatmap; in sparse regions,
#' they will show the actual points.
#'
#' @param x vector containing the X coordinates of the points.
#' @param y vector containing the Y coordinates of the points.
#' which are passed on to the plotting function so the plot can be
#' customized.
#' @param nbins how many bins to use for histogram.
#' @param max.points when to start plotting heatmap instead of
#' individual points.
#' @param colfunc color mapping for heatmap.
#' @param tf.fun function to apply to histogram values before
#' plotting them, \code{log} can be useful.
#' @param pch point character to use for individual points.
#' @param cex point character expansion to use for individual points.
#' @param color.levels how many different colors to use for heatmap.
#' @param ... further arguments to be passed to the plotting function,
#' such as \code{xlim} or \code{xlab}.
#' @examples
#' densityplot2d( facsdata[,1], facsdata[,2], tf.fun=sqrt, nbin=100 )
#' pc <- princomp(facsdata)
#' densityplot2d( pc$scores[,1], pc$scores[,2], tf.fun=sqrt, nbin=100 )
#' densityplot2d( pc$scores[,1], pc$scores[,2], z=facsdata[,2],tf.fun=sqrt, nbin=100, colfunc=colorRampPalette(c("black","red")) )
#' @export
densityplot2d <- function( x, y, z=NULL, nbins=50, max.points=5,
colfunc=colorRampPalette(c("gray","darkblue","lightblue", "green", "yellow","orange", "red")), tf.fun=identity, pch=19, cex=0.3, color.levels=20,
... ){
x <- as.numeric(x)
y <- as.numeric(y)
if( any( is.na(x) ) ){
stop("NA values in X!")
}
if( any( is.na(y) ) ){
stop("NA values in Y!")
}
x.args <- list(...)
if( "xlim" %in% names(x.args) ){
x.bin <- seq(x.args$xlim[1],x.args$xlim[2],length=nbins+1)
x.args[["xlim"]] <- NULL
} else {
x.bin <- seq(floor(min(x)-.001), ceiling(max(x)+.001), length=(nbins+1))
}
if( "ylim" %in% names(x.args) ){
y.bin <- seq(x.args$ylim[1],x.args$ylim[2],length=nbins+1)
x.args[["ylim"]] <- NULL
} else {
y.bin <- seq(floor(min(y)-.001), ceiling(max(y)+.001), length=(nbins+1))
}
x.int <- as.integer(findInterval( x, x.bin ))
y.int <- as.integer(findInterval( y, y.bin ))
pix <- (x.int-1)*(nbins) + (y.int)
density.matrix <- tabulate(pix)
density.matrix <- c(density.matrix,rep(0,nbins^2-length(density.matrix)))
density.matrix <- matrix( density.matrix, nrow=nbins, byrow=TRUE )
points.not.in.image <- density.matrix[cbind(x.int,y.int)] <= max.points
#stopifnot( all( density.matrix[cbind(x.int,y.int)] > 0 ) )
density.matrix[density.matrix <= max.points] <- NA
if( !is.null( z ) ){
z.matrix <- tapply( z, pix, mean )[as.character(1:(nbins^2))]
z.matrix <- matrix( z.matrix, nrow=nbins, byrow=TRUE )
}
do.call(plot,c(list(NA,
xlim=range(x.bin),
ylim=range(y.bin)),x.args))
M <- tf.fun( density.matrix )
zlim <- range(M[is.finite(M)])
M <- (M - zlim[1L])/diff(zlim)
col <- colfunc( color.levels )
if( !is.null(z) ){
zp <- z[points.not.in.image]
zlim <- range( z.matrix[is.finite(z.matrix)] )
z.matrix <- 1-(z.matrix-zlim[1L])/diff(zlim)
zi <- floor((color.levels - 1e-05) * z.matrix + 1e-07)
zi[zi < 0 | zi >= color.levels] <- NA
zp <- 1-(zp-zlim[1L])/diff(zlim)
zp <- floor((color.levels - 1e-05) * zp + 1e-07)
zp[zp < 0 | zp >= color.levels] <- NA
}
Mi <- floor((color.levels - 1e-05) * M + 1e-07)
Mi[Mi < 0 | Mi >= color.levels] <- NA
if( is.null(z) ){
#1D lookup
Mc <- col[Mi+1L]
} else {
#2D lookup
col <- sapply(rev(col),function(x)
colorRampPalette(c("white",x))(color.levels+1) )
col <- col[2:(color.levels+1),,drop=FALSE]
Mc <- col[cbind(c(Mi+1L),c(zi+1L))]
}
dim(Mc) <- dim(M)
rasterImage( t(Mc)[nrow(Mc):1, ,drop=FALSE],
min(x.bin), min(y.bin), max(x.bin), max(y.bin), interpolate=FALSE )
if( is.null( z ) ){
points( x[points.not.in.image], y[points.not.in.image],
pch=pch, cex=cex, col=colfunc(color.levels)[1] )
} else {
points( x[points.not.in.image], y[points.not.in.image],
pch=pch, cex=cex, col=col[1,zp+1] )
}
#x.points <- c(0,1,2,5,10,20,50,100,200,500,1000)
#axis( 1, log( 1+x.points, base=2 ), as.character(x.points) )
#axis( 2, log( 1+x.points, base=2 ), as.character(x.points) )
}
#' @export
confregion2d <- function(M, max.points=1000, alpha=0.05){
requireNamespace("depth",quietly=TRUE)
if( nrow(M) > max.points ){
M <- M[sample(1:nrow(M),max.points,replace=TRUE),]
}
d <- apply( M, 1, function(x) depth::depth(x,M) )
dt <- quantile( d, alpha )
M <- M[d>dt,]
h <- chull( M )
M[c(h,h[1]),]
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.