# BACKGROUND-NORMEXP.R
normexp.signal <- function(par,x)
# Expected value of signal given foreground in normal + exponential model
# Gordon Smyth
# 24 Aug 2002. Last modified 24 February 2012.
{
mu <- par[1]
sigma <- exp(par[2])
sigma2 <- sigma*sigma
alpha <- exp(par[3])
# cat(c(mu,sigma,alpha),"\n")
if(alpha <= 0) stop("alpha must be positive")
if(sigma <= 0) stop("sigma must be positive")
mu.sf <- x-mu-sigma2/alpha
signal <- mu.sf + sigma2 * exp(dnorm(0,mean=mu.sf,sd=sigma,log=TRUE) - pnorm(0,mean=mu.sf,sd=sigma,lower.tail=FALSE,log.p=TRUE))
o <- !is.na(signal)
if(any(signal[o]<0)) {
warning("Limit of numerical accuracy reached with very low intensity or very high background:\nsetting adjusted intensities to small value")
signal[o] <- pmax(signal[o],1e-6)
}
signal
}
normexp.fit <- function(x, method="saddle", n.pts=NULL, trace=FALSE)
# Estimate parameters of normal+exponential convolution model
# Pure R Version Gordon Smyth 24 Aug 2002.
# Version with C by Jeremy Silver 29 Oct 2007.
# Last modified 14 January 2015.
{
isna <- is.na(x)
if(any(isna)) x <- x[!isna]
if(length(x)<4) stop("Not enough data: need at least 4 non-missing corrected intensities")
if(trace) cat("trace not currently implemented\n")
method <- match.arg(method,c("mle","saddle","rma","rma75","mcgee","nlminb","nlminblog"))
# Backward compatility with old names
if(method=="mcgee") method <- "rma75"
if(method=="nlminb") method <- "mle"
if(method=="nlminblog") method <- "mle"
if(method=="rma") {
if(!requireNamespace("affy",quietly=TRUE)) stop("affy package required but is not available")
out <- affy::bg.parameters(x)
return(list(par=c(out$mu,log(out$sigma),-log(out$alpha))))
}
if(method=="rma75") {
out <- .bg.parameters.rma75(x)
return(list(par=c(out$mu,log(out$sigma),-log(out$alpha))))
}
# Starting values for parameters mu, alpha and sigma
q <- quantile(x, c(0,0.05,0.1,1), na.rm = TRUE, names = FALSE)
if(q[1]==q[4]) return(list(par=c(q[1],-Inf,-Inf),m2loglik=NA,convergence=0))
if(q[2] > q[1]) {
mu <- q[2]
} else {
if(q[3] > q[1]) {
mu <- q[3]
} else {
mu <- q[1] + 0.05*(q[4]-q[1])
}
}
sigma2 <- mean((x[x<mu]-mu)^2, na.rm = TRUE)
alpha <- mean(x,na.rm = TRUE) - mu
if(alpha <= 0) alpha <- 1e-6
Par0 <- c(mu,log(sigma2)/2,log(alpha))
# Use a maximum of n.pts points for the fit
if(!is.null(n.pts)) if(n.pts >= 4 & n.pts < length(x)) {
a <- 0.5
x <- quantile(x,((1:n.pts)-a)/n.pts,type=5)
}
# Maximize saddlepoint approximation to likelihood
out1 <- .C("fit_saddle_nelder_mead",
par = as.double(Par0),
X = as.double(x),
N = as.integer(length(x)),
convergence = as.integer(0),
fncount = as.integer(0),
m2loglik = as.double(0),
PACKAGE="limma")
out1$X <- out1$N <- NULL
if(method=="saddle") return(out1)
Par1 <- out1$par
# Convert from log-sd to log-var parametrization
Par1[2] <- 2*Par1[2]
LL1 <- .normexp.m2loglik(Par1, f = x)
out2 <- nlminb(start = Par1,
objective = .normexp.m2loglik,
gradient = .normexp.gm2loglik,
hessian = .normexp.hm2loglik,
f = x,
scale = median(abs(Par1))/abs(Par1))
# Convert back to log-sd parametrization
out2$par[2] <- out2$par[2]/2
out2$m2loglik <- out2$objective
out2$objective <- NULL
# Check whether nlminb helped
if(out2$m2loglik >= LL1) return(out1)
out2
}
.normexp.m2loglik <- function(theta,f)
# normexp minus-twice log-likelihood
# Jeremy Silver
# 29 Oct 2007.
# Last modified 25 Sept 2008.
{
mu <- theta[1]
s2 <- exp(theta[2])
al <- exp(theta[3])
.C("normexp_m2loglik",
mu = as.double(mu),
s2 = as.double(s2),
al = as.double(al),
n = as.integer(length(f)),
f = as.double(f),
m2LL = double(1),
PACKAGE="limma"
)$m2LL
}
.normexp.gm2loglik <- function(theta,f)
# Gradient of normexp m2loglik
# with respect to mu, log(sigma^2) and log(alpha)
# Jeremy Silver
# 29 Oct 2007.
# Last modified 25 Sept 2008.
{
mu <- theta[1]
s2 <- exp(theta[2])
al <- exp(theta[3])
.C("normexp_gm2loglik",
mu = as.double(mu),
s2 = as.double(s2),
al = as.double(al),
n = as.integer(length(f)),
f = as.double(f),
dm2LL = double(3),
PACKAGE = "limma"
)$dm2LL
}
.normexp.hm2loglik <- function(theta,f)
# Hessian of normexp m2loglik
# with respect to mu, log(sigma^2) and log(alpha)
# Jeremy Silver
# 29 Oct 2007.
# Last modified 25 Sept 2008.
{
mu <- theta[1]
s2 <- exp(theta[2])
al <- exp(theta[3])
matrix(.C("normexp_hm2loglik",
mu = as.double(mu),
s2 = as.double(s2),
al = as.double(al),
n = as.integer(length(f)),
f = as.double(f),
d2m2LL = double(9),
PACKAGE="limma"
)$d2m2LL,3,3)
}
.bg.parameters.rma75 <- function(pm,n.pts = 2^14)
# Estimate normexp parameters
# This code is extracted without alteration from the RMA-75 function of
# McGee, M. and Chen, Z. (2006). Parameter estimation for the
# exponential-normal convolution model for background correction
# of Affymetrix GeneChip data.
# Stat Appl Genet Mol Biol, 5(1), Article 24.
{
## mu-correction function
mu.est.correct <- function(m,s,a) {
f <- function(x) (dnorm(x-s*a)-s*a*(pnorm(x-s*a)+pnorm(m/s+s*a)-1))
t <- uniroot(f, c(-5, 10), tol = 1e-12)$root
t <- m-s*t
return(t)
}
## getting mode function
max.density <- function(x, n.pts) {
aux <- density(x, kernel = "epanechnikov", n = n.pts, na.rm = TRUE)
aux$x[order(-aux$y)[1]]
}
pmbg <- max.density(pm, n.pts)
bg.data <- pm[pm < pmbg]
pmbg <- max.density(bg.data, n.pts)
mubg <- pmbg ## the mode
bg.data <- pm[pm < pmbg]
bg.data <- bg.data - pmbg
bgsd <- sqrt(sum(bg.data^2)/(length(bg.data) - 1)) * sqrt(2) ## estimate sigma
sig.data<-pm[pm > pmbg]
sig.data <- sig.data - pmbg
q75 <- 0.75
alpha3 <- -(quantile(pm,q75)-pmbg)/log(1-q75) ## 75th quantile estimation
## mode-correction
mu3 <- mu.est.correct(m=mubg,s=bgsd,a=1/alpha3)
mu3 <- (mu3+mubg)/2 ## take ave
bg.data3<- pm[pm < mu3]
bg.data3 <- bg.data3 - mu3
bgsd3 <- sqrt(sum(bg.data3^2)/(length(bg.data3) - 1)) * sqrt(2)
sig.data3 <- pm[pm > mu3]
alpha3<- -(quantile(pm,q75)-mu3)/log(1-q75)
list(alpha = 1/alpha3, mu = mu3, sigma = bgsd3)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.