Nothing
#' Standard plot method for objects of class 'RWDRI'
#'
#' @param x (object) of class 'RWDRI'
#' @param Scale (character) specifying if percentiles are calculated on the original scale ("or") or the transformed scale ("tr") or the z-Score scale ("z")
#' @param RIperc (numeric) value specifying the percentiles, which define the reference interval (default c(0.025, 0.975))
#' @param Nhist (integer) number of bins in the histogram (derived automatically if not set)
#' @param showCI (logical) specifying if the confidence intervals are shown
#' @param showPathol (logical) specifying if the estimated pathological distribution shall be shown
#' @param scalePathol (logical) specifying if the estimated pathological distribution shall be weighted with the ration of pathol/non-pathol
#' @param showBSModels (logical) specifying if the estimated bootstrapping models shall be shown
#' @param showValue (logical) specifying if the exact value of the estimated reference intervals shall be shown above the plot
#' @param CIprop (numeric) value specifying the central region for estimation of confidence intervals
#' @param pointEst (character) specifying the point estimate determination: (1) using the full dataset ("fullDataEst"),
#' (2) calculating the median model from the bootstrap samples ("medianBS"), (2) works only if NBootstrap > 0
#' @param xlim (numeric) vector specifying the limits in x-direction
#' @param ylim (numeric) vector specifying the limits in y-direction
#' @param xlab (character) specifying the x-axis label
#' @param ylab (character) specifying the y-axis label
#' @param title (character) specifying plot title
#' @param ... additional arguments passed forward to other functions
#'
#' @return The applied plot limits in x-direction (xlim) are returned.
#'
#' @author Christopher Rank \email{christopher.rank@@roche.com}, Tatjana Ammer \email{tatjana.ammer@@roche.com}
#'
#' @method plot RWDRI
plot.RWDRI <- function(x, Scale = c("original", "transformed", "zScore"), RIperc = c(0.025, 0.975), Nhist = 60, showCI = TRUE, showPathol = FALSE, scalePathol = TRUE, showBSModels = FALSE, showValue = TRUE,
CIprop = 0.95, pointEst = c("fullDataEst", "medianBS"), xlim = NULL, ylim = NULL, xlab = NULL, ylab = NULL, title = NULL, ...) {
stopifnot(class(x) == "RWDRI")
stopifnot(!is.null(x$Data))
Scale <- match.arg(Scale[1], choices = c("original", "transformed", "zScore"))
stopifnot(is.numeric(RIperc) & min(RIperc)>=0 & max(RIperc)<=1)
stopifnot(is.numeric(CIprop) & length(CIprop)==1 & CIprop>=0 & CIprop<=1)
stopifnot(is.numeric(Nhist) & Nhist%%1==0 & Nhist>0)
pointEst <- match.arg(pointEst[1], choices = c("fullDataEst", "medianBS"))
stopifnot(is.null(xlim) | (is.numeric(xlim) & length(xlim)==2))
stopifnot(is.null(ylim) | (is.numeric(ylim) & length(ylim)==2))
stopifnot(is.logical(showCI))
stopifnot(is.logical(showPathol))
stopifnot(is.logical(scalePathol))
stopifnot(is.logical(showBSModels))
stopifnot(is.logical(showValue))
modelFound <- (!is.na(x$Mu) & !is.na(x$Sigma) & !is.na(x$Lambda) & !is.na(x$Shift))
BSPerformed <- (modelFound & length(x$MuBS) > 0 & length(x$SigmaBS) > 0 & length(x$LambdaBS) > 0 & length(x$ShiftBS) > 0)
showBSModels <- ifelse(BSPerformed & Scale=="original", showBSModels, FALSE)
# extract binned data
Data <- x$Data
# extract model parameters
if(modelFound)
{
lambda <- ifelse(pointEst=="medianBS" & BSPerformed, x$LambdaMed, x$Lambda)
mu <- ifelse(pointEst=="medianBS" & BSPerformed, x$MuMed, x$Mu)
sigma <- ifelse(pointEst=="medianBS" & BSPerformed, x$SigmaMed, x$Sigma)
shift <- ifelse(pointEst=="medianBS" & BSPerformed, x$ShiftMed, x$Shift)
P <- ifelse(pointEst=="medianBS" & BSPerformed, x$PMed, x$P)
}
# calculate reference intervals
RI <- getRI(x = x, RIperc = RIperc, CIprop = CIprop, pointEst = pointEst, Scale = Scale)
if (is.null(xlab))
xlab <- "Concentration [Units]"
if (is.null(ylab))
ylab <- "Frequency"
# transform data to the correct scale
if(modelFound & (Scale == "transformed" | Scale == "zScore"))
{
Data <- suppressWarnings(BoxCox(Data - shift, lambda))
Data <- Data[!is.na(Data) & is.finite(Data)]
if(Scale == "zScore")
Data <- (Data - mu) / sigma
}
# determine reasonable xlim
if (is.null(xlim))
{
if (!modelFound)
{
rangeData <- as.numeric(quantile(x = Data, probs = c(0.005, 0.995), na.rm = TRUE))
rangeData <- rangeData + c(-0.02, 0.02)*diff(rangeData)
} else if(modelFound & (Scale == "transformed" | Scale == "zScore"))
{
rangeData <- as.numeric(quantile(x = Data, probs = c(0.001, 0.999), na.rm = TRUE))
} else
{
# calculate skewness of distribution
skewnessRatio <- diff(getRI(x, RIperc=c(0.025, 0.5, 0.975))$PointEst)
skewnessRatio <- min(1, sqrt(skewnessRatio[1]/skewnessRatio[2]))
# estimate appropriate concentration range for distribution
perc595 <- getRI(x, RIperc=c(0.05, 0.95-0.04*(1-skewnessRatio)))$PointEst
rangeData <- perc595 + c(-1, 1)*1.05*diff(perc595)
# determine appropriate min and max of dataset
minData <- max(1e-20, quantile(x=Data, probs=0.005, na.rm=TRUE))
maxData <- quantile(x=Data, probs=0.995, na.rm=TRUE)
# shift range outside of the NP distribution to the right or left when covered by the dataset
if(rangeData[1] < minData)
{
rangeData[2] <- min(rangeData[2]+minData-rangeData[1], maxData)
rangeData[1] <- minData
}
if(rangeData[2] > maxData)
{
rangeData[1] <- max(rangeData[1]+maxData-rangeData[2], minData)
rangeData[2] <- maxData
}
rangeData <- rangeData + c(-0.02, 0.02)*diff(rangeData)
rangeData[2] <- min(max(Data), rangeData[2])
}
rangePE <- range(RI$PointEst)
rangePE <- rangePE + c(-0.06, 0.06)*diff(rangePE)
rangeCI <- range(RI$CILow, RI$CIHigh)
rangeCI <- rangeCI + c(-0.03, 0.03)*diff(rangeCI)
xlim <- range(rangeData, rangePE, rangeCI, na.rm=TRUE)
if(Scale == "original")
xlim[1] <- max(xlim[1], 1e-20)
}
if (is.null(title))
title <- paste0("Estimated Reference Interval")
if(is.na(x$roundingBase) | Scale == "transformed" | Scale == "zScore")
{
# generate histogram of data
increment <- diff(xlim)/Nhist
breaks1 <- seq(from = xlim[1] - Nhist*increment, to = xlim[2] + Nhist*increment, by = increment)
breaks2 <- breaks1 + 0.5*increment
if(Scale == "original")
{
breaks1 <- breaks1[breaks1 > 1e-20]
breaks2 <- breaks2[breaks2 > 1e-20]
}
hist1 <- hist(Data[Data >= min(breaks1) & Data <= max(breaks1)], breaks = breaks1, plot = FALSE)
hist2 <- hist(Data[Data >= min(breaks2) & Data <= max(breaks2)], breaks = breaks2, plot = FALSE)
countsData <- c(hist1$counts, hist2$counts)
mids <- c(hist1$mids, hist2$mids)
# sort vectors in increasing order
sortIndex <- sort(mids, index.return = TRUE)$ix
countsData <- countsData[sortIndex]
mids <- mids[sortIndex]
# combine data from hist1 and hist2 that histograms overlap
hist1$breaks <- c(mids - 0.25*increment, mids[length(mids)] + 0.25*increment)
hist1$counts <- countsData
hist1$density <- countsData/sum(countsData)
hist1$mids <- mids
breakL <- breakLBS <- c(breaks1[1:(length(breaks1)-1)], breaks2[1:(length(breaks2)-1)])
breakR <- breakRBS <- c(breaks1[2:length(breaks1)], breaks2[2:length(breaks2)])
} else
{
xlimDiff <- diff(xlim)
binSize <- x$roundingBase*max(1, round(xlimDiff/x$roundingBase/Nhist))
# adapt xlim
xlim[1] <- max(0.5*x$roundingBase, round(xlim[1]/x$roundingBase)*x$roundingBase - 0.5*x$roundingBase)
xlim[2] <- xlim[1] + ceiling(xlimDiff/binSize)*binSize
breaks1 <- seq(from=xlim[1], to=xlim[2], by=binSize)
hist1 <- hist(Data[Data >= min(breaks1) & Data <= max(breaks1)], breaks = breaks1, plot = FALSE)
sortIndex <- 1:length(hist1$mids)
mids <- hist1$mids
countsData <- hist1$counts
breakL <- breakLBS <- breaks1[1:(length(breaks1)-1)]
breakR <- breakRBS <- breaks1[2:length(breaks1)]
}
# Box Cox transformation of histogram breaks and histogram range
if (Scale == "original" & modelFound) {
breakL <- suppressWarnings(BoxCox(breakL-shift, lambda=lambda))
breakR <- suppressWarnings(BoxCox(breakR-shift, lambda=lambda))
}
# calculate curves of BS models
if (BSPerformed & showBSModels)
{
countsPredBS <- matrix(NA, nrow=length(breakLBS), ncol=length(x$MuBS))
for(i in 1:length(x$MuBS))
{
breakLBS_i <- suppressWarnings(BoxCox(breakLBS-x$ShiftBS[i], lambda=x$LambdaBS[i]))
breakRBS_i <- suppressWarnings(BoxCox(breakRBS-x$ShiftBS[i], lambda=x$LambdaBS[i]))
pCorrBS <- BoxCox(c(max(min(x$Data-x$ShiftBS[i]), 1e-20), min(max(x$Data-x$ShiftBS[i]), 1e20)), lambda=x$LambdaBS[i])
pCorrBS <- pnorm(q=pCorrBS, mean=x$MuBS[i], sd=x$SigmaBS[i])
pCorrBS <- 1/(pCorrBS[2]-pCorrBS[1])
tempCountsPredBS <- pCorrBS*length(Data)*x$PBS[i]*(pnorm(q = breakRBS_i, mean = x$MuBS[i], sd = x$SigmaBS[i]) - pnorm(q = breakLBS_i, mean = x$MuBS[i], sd = x$SigmaBS[i]))
tempCountsPredBS[tempCountsPredBS < 0] <- 0
tempCountsPredBS <- tempCountsPredBS[sortIndex]
countsPredBS[, i] <- tempCountsPredBS
}
}
maxPred <- NA
if (modelFound) {
pCorr <- BoxCox(c(max(min(x$Data-shift), 1e-20), min(max(x$Data-shift), 1e20)), lambda=lambda)
pCorr <- pnorm(q=pCorr, mean=mu, sd=sigma)
pCorr <- 1/(pCorr[2]-pCorr[1])
if(Scale == "zScore")
countsPred <- pCorr*length(Data)*P*(pnorm(q = breakR, mean = 0, sd = 1) - pnorm(q = breakL, mean = 0, sd = 1))
else
countsPred <- pCorr*length(Data)*P*(pnorm(q = breakR, mean = mu, sd = sigma) - pnorm(q = breakL, mean = mu, sd = sigma))
countsPred[countsPred < 0] <- 0
countsPred <- countsPred[sortIndex]
maxPred <- max(countsPred)
# calculate difference of counts
countsDiff <- countsData - countsPred
countsDiff[countsDiff<0] <- 0
if (scalePathol) {
# calculate weighting of difference
weightDiff <- countsDiff/(countsPred+1e-20)
weightDiff <- pmin(weightDiff, 1.0)
countsDiff <- countsDiff*weightDiff
}
}
if (is.null(ylim)) {
ylim <- c(0, 1.03*max(countsData, maxPred, na.rm = TRUE))
ylim[1] <- 0.03*ylim[2]
}
plot(hist1, freq = TRUE, border = NA, col = "grey80", main = title, xaxt = 'n', yaxt = 'n', xlab = xlab, ylab = ylab, xlim = xlim, ylim = ylim, cex.main = 1.3, cex.lab = 1.05)
axis(1, at = pretty(xlim, n = 10), cex.axis = 0.85)
axis(2, at = pretty(ylim), las = 1, cex.axis = ifelse(2/3*max(ylim) < 1e5, 0.75, 0.70), mgp = c(3, ifelse(2/3*max(ylim) < 1e4, 1.0, 0.8), 0))
# add curves of BS models
if (showBSModels) {
for (i in 1:length(x$MuBS)) {
lines(x = mids, y = countsPredBS[, i], lwd = 2, col = as.rgb("green3", min(0.25, 4/length(x$MuBS))))
}
}
# add curve of total distribution
lines(x = mids, y = countsData, lty = 2, lwd = 1.5, col = "black")
if (modelFound) {
# add curve of non-pathological distribution
if(!showBSModels)
lines(x = mids, y = countsPred, lwd = 2, col = "green3")
# add curve of pathological distribution
if(showPathol & !showBSModels)
lines(mids, countsDiff, col = "red4", lwd = 1.5)
}
addGrid(pretty(xlim, n = 10), pretty(ylim), col = "grey90")
box()
# add confidence intervals
if (modelFound) {
for (i in 1:length(RIperc)) {
if (showCI & !is.na(RI$CILow[i]) & !is.na(RI$CIHigh[i]))
rect(RI$CILow[i], -1e3, RI$CIHigh[i], 1e9, col = as.rgb("green2", 0.20), border = NA)
}
abline(v = RI$PointEst, lwd = 2, lty = 2, col = "green3")
selection <- which(RI$PointEst>par("usr")[1] & RI$PointEst<par("usr")[2])
if(showValue & length(selection)>0)
{
adjust <- rep(0.5, times = length(RIperc))
adjust[RI$Percentile < 0.5] <- 1
adjust[RI$Percentile > 0.5] <- 0
mtext(text = signif(RI$PointEst[selection], 3), at = RI$PointEst[selection], col = "green3", cex = 1.0, adj = adjust[selection])
}
}
return(invisible(xlim))
}
#' Add a grid to an existing plot.
#'
#' It is possible to use automatically determined grid lines (\code{x=NULL, y=NULL}) or specifying the number
#' of cells \code{x = 3, y = 4} as done by \code{grid}. Additionally, x- and y-locations of grid-lines can be specified,
#' e.g. \code{x = 1:10, y = seq(0,10,2)}.
#'
#' @param x (integer, numeric) single integer specifies number of cells, numeric vector specifies vertical grid-lines
#' @param y (integer, numeric) single integer specifies number of cells, numeric vector specifies horizontal grid-lines
#' @param col (character) color of grid-lines
#' @param lwd (integer) line width of grid-lines
#' @param lty (integer) line type of grid-lines
#'
#' @author Andre Schuetzenmeister \email{andre.schuetzenmeister@@roche.com}
addGrid <- function(x = NULL, y = NULL, col = "lightgray", lwd = 1L, lty = 3L) {
if (all(is.null(c(x,y))) || all(length(c(x,y))<2)) # call grid function
grid(nx = x, ny = y, col = col, lwd = lwd, lty = lty)
else {
if (length(x) == 0) # NULL
xticks <- axTicks(side=1)
else if (length(x) == 1) {
U <- par("usr")
xticks <- seq.int(U[1L], U[2L], length.out = x + 1)
} else
xticks <- x
if (length(y) == 0) # NULL
yticks <- axTicks(side = 2)
else if (length(y) == 1) {
U <- par("usr")
yticks <- seq.int(U[3L], U[4L], length.out = y + 1)
}
else
yticks <- y
abline(v = xticks, col = col, lwd = lwd, lty = lty)
abline(h = yticks, col = col, lwd = lwd, lty = lty)
}
}
#' Convert color-names or RGB-code to possibly semi-transparent RGB-code.
#'
#' Function takes the name of a color and converts it into the rgb space. Parameter "alpha" allows
#' to specify the transparency within [0,1], 0 meaning completey transparent and 1 meaning completey
#' opaque. If an RGB-code is provided and alpha != 1, the RGB-code of the transparency adapted color
#' will be returned.
#'
#' @param col (character) name of the color to be converted/transformed into RGB-space (code). Only
#' those colors can be used which are part of the set returned by function colors(). Defaults
#' to "black".
#' @param alpha (numeric) value specifying the transparency to be used, 0 = completely transparent,
#' 1 = opaque.
#'
#' @return RGB-code
#'
#' @author Andre Schuetzenmeister \email{andre.schuetzenmeister@@roche.com}
#'
#' @examples
#' \dontrun{
#' # convert character string representing a color to RGB-code using alpha-channel of .25 (75\% transparent)
#' as.rgb("red", alpha = .25)
#'
#' # same thing now using the RGB-code of red (alpha=1, i.e. as.rgb("red"))
#' as.rgb("#FF0000FF", alpha = .25)
#' }
as.rgb <- function(col = "black", alpha = 1) {
if (length(col) > 1 && (length(alpha) == 1 || length(alpha) < length(col))) { # unclear which alpha to use or only one alpha specified
if(length(alpha) < length(col) && length(alpha) > 1)
warning("Multiple (but too few) 'alpha' specified! Only use 'alpha[1]' for each color!")
return(sapply(col, as.rgb, alpha = alpha[1]))
}
if (length(col) > 1 && length(col) <= length(alpha)) { # process each color separately
res <- character()
for (i in 1:length(col))
res <- c(res, as.rgb(col[i], alpha[i]))
return(res)
}
if ( col %in% colors() )
return( rgb(t(col2rgb(col))/255, alpha = alpha) )
else {
col <- sub("#", "", col)
R <- as.numeric(paste("0x", substr(col, 1,2), sep = ""))
G <- as.numeric(paste("0x", substr(col, 3,4), sep = ""))
B <- as.numeric(paste("0x", substr(col, 5,6), sep = ""))
return( rgb(R/255, G/255, B/255, alpha = alpha, maxColorValue = 1) )
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.