Nothing
## EM for Beta Mixture Models (BMM) with Nc components
EM_bmm_mun <- function(x, Nc, mix_init, Ninit=50, verbose=FALSE, Niter.max=500, tol, Neps, eps=c(w=0.005,m=0.005,N=0.005))
{
N <- length(x)
assert_that(N+Nc >= Ninit)
## check data for 0 and 1 values which are problematic, but may be
## valid, depending on a and b. Moving these to eps or 1-eps
## ensures proper handling during fit.
x0 <- x==0
if(any(x0)) {
message("Detected ", sum(x0), " value(s) which are exactly 0.\nTo avoid numerical issues during EM such values are moved to smallest eps on machine.")
x[x0] <- .Machine$double.eps
}
x1 <- x==1
if(any(x1)) {
message("Detected ", sum(x1), " value(s) which are exactly 1.\nTo avoid numerical issues during EM such values are moved to one minus smallest eps on machine.")
x[x1] <- 1-.Machine$double.eps
}
## temporaries needed during EM
LxO <- matrix(logit(x), ncol=Nc, nrow=N)
LxC <- matrix(log1p(-x), ncol=Nc, nrow=N)
sLxO <- colSums(LxO)
sLxC <- colSums(LxC)
xRep <- rep(x, each=Nc)
## initialize randomly using KNN
if(missing(mix_init)) {
## assume that the sample is ordered randomly
ind <- seq(1,N-Nc,length=Ninit)
knnInit <- list(mu=matrix(0,nrow=Nc,ncol=1), p=rep(1/Nc, times=Nc))
for(k in seq(Nc))
knnInit$mu[k,1] <- mean(x[ind+k-1])
KNN <- suppressWarnings(knn(x, K=Nc, init=knnInit, Niter.max=50))
muInit <- rep(mean(x), times=Nc)
varInit <- rep(1.5*var(x), times=Nc)
for(k in 1:Nc) {
kind <- KNN$cluster == k
if(sum(kind) > 10) {
muInit[k] <- KNN$center[k]
varInit[k] <- var(x[kind])
}
}
nInit <- pmax(muInit*(1-muInit)/varInit - 1, 1, na.rm=TRUE)
## place the component which recieved the least weight at the
## data center with roughly the variance of the sample
cmin <- which.min(KNN$p)
muInit[cmin] <- sum(KNN$p * KNN$center)
## muInit[cmin] <- mean(x) ## could be considered here
nInit[cmin] <- pmax(muInit[cmin]*(1-muInit[cmin])/var(x) - 1, 1, na.rm=TRUE)
##Nmax <- max(2, max(nInit))
## ensure n is positive for each cluster; if this is not the
## case, sample uniformly from the range of n we have
##Nneg <- nInit <= .Machine$double.eps
##Nsmall <- nInit <= 0.5
##if(any(Nsmall))
## nInit[Nsmall] <- runif(sum(Nsmall), 0.5, Nmax)
##nInitR <- 0.5 + rlnorm(Nc, log(nInit), log(5)/1.96)
##mixEst <- rbind(KNN$p, nInitR*muInit, nInitR*(1-muInit))
mixEst <- rbind(KNN$p, nInit*muInit, nInit*(1-muInit))
dlink(mixEst) <- identity_dlink
rownames(mixEst) <- c("w", "a", "b")
} else {
mixEst <- mix_init
}
if(verbose) {
message("EM for beta mixture model.\n")
message("Initial estimates:\n")
print(mixEst)
}
## mixEst parametrization during fitting
mixEstPar <- mixEst
mixEstPar[1,] <- logit(mixEst[1,,drop=FALSE])
mixEstPar[2,] <- logit(mixEst[2,] / (mixEst[2,] + mixEst[3,]))
mixEstPar[3,] <- log(mixEst[2,] + mixEst[3,])
rownames(mixEstPar) <- c("w", "Lm", "lN")
## in case tolerance is not specified, then this criteria is
## ignored
if(missing(tol)) {
checkTol <- FALSE
tol <- -1
} else
checkTol <- TRUE
if(missing(Neps)) {
## in case tolerance has been declared, but Neps not, we flag
## to disable checking of running mean convergence check
checkEps <- FALSE
Neps <- 5
} else
checkEps <- TRUE
## if nothing is specified, we declare convergence based on a
## running mean of differences in parameter estimates
if(!checkTol & !checkEps) {
checkEps <- TRUE
}
assert_that(Neps > 1)
assert_that(ceiling(Neps) == floor(Neps))
## eps can also be given as a single integer which is interpreted
## as number of digits
if(length(eps) == 1) eps <- rep(10^(-eps), 3)
iter <- 0
logN <- log(N)
traceMix <- list()
traceLli <- c()
Dlli <- Inf
runMixPar <- array(-Inf, dim=c(Neps,3,Nc), dimnames=list(NULL, rownames(mixEstPar), NULL ))
runOrder <- 0:(Neps-1)
Npar <- Nc + 2*Nc
if(Nc == 1) Npar <- Npar - 1
## find alpha and beta for a given component in log-space
bmm_mun_ml <- function(c1,c2) {
function(par) {
mu <- inv_logit(par[1])
nmu <- inv_logit(-par[1]) ## 1-mu
n <- exp(par[2])
ab <- c(mu * n, nmu * n)
di <- digamma(ab[2])
eq1 <- digamma(ab[1]) - di
eq2 <- di - digamma(n)
(eq1 - c1)^2 + (eq2 - c2)^2
}
}
while(iter < Niter.max) {
## calculate responsabilities from the likelihood terms;
## calculations are done in log-space to avoid numerical
## difficulties if some points are far away from some
## component and hence recieve very low density
lli <- t(matrix(log(mixEst[1,]) + dbeta(xRep, mixEst[2,], mixEst[3,], log=TRUE), nrow=Nc))
lnresp <- matrixStats::rowLogSumExps(lli)
## ensure that the log-likelihood does not go out of numerical
## reasonable bounds
lli <- apply(lli, 2, pmax, -30)
## the log-likelihood is then given by the sum of lresp norms
lliCur <- sum(lnresp)
## record current state
traceMix <- c(traceMix, list(mixEst))
traceLli <- c(traceLli, lliCur)
if(iter > 1) {
## Dlli is the slope of the log-likelihood evaulated with
## a second order method
Dlli <- (traceLli[iter+1] - traceLli[iter - 1])/2
}
if(Nc > 1) {
smean <- apply(runMixPar[order(runOrder),,,drop=FALSE], c(2,3), function(x) mean(abs(diff(x))))
eps.converged <- sum(sweep(smean, 1, eps, "-") < 0)
} else {
smean <- apply(runMixPar[order(runOrder),-1,,drop=FALSE], c(2,3), function(x) mean(abs(diff(x))))
eps.converged <- sum(sweep(smean, 1, eps[-1], "-") < 0)
}
if(is.na(eps.converged)) eps.converged <- 0
if(verbose) {
message("Iteration ", iter, ": log-likelihood = ", lliCur, "; Dlli = ", Dlli, "; converged = ", eps.converged, " / ", Npar, "\n", sep="")
}
if(checkTol & Dlli < tol) {
break
}
if(iter >= Neps & checkEps & eps.converged == Npar) {
break
}
## ... and the (log) responseability matrix follows from this by
## appropiate normalization.
lresp <- sweep(lli, 1, lnresp, "-")
resp <- exp(lresp)
## mean probability to be in a specific mixture component -> updates
## mixEst first row
lzSum <- matrixStats::colLogSumExps(lresp)
zSum <- exp(lzSum)
mixEst[1,] <- exp(lzSum - logN)
## make sure it scales exactly to 1 which may not happen due
## to small rounding issues
mixEst[1,] <- mixEst[1,] / sum(mixEst[1,])
c1 <- colSums(LxO * resp)/zSum
c2 <- colSums(LxC * resp)/zSum
## now solve for new alpha and beta estimates jointly for each
## component
for(i in 1:Nc) {
Lest <- optim(mixEstPar[c(2,3),i], bmm_mun_ml(c1[i], c2[i]))
if(Lest$convergence != 0) {
warning("Warning: Component", i, "in iteration", iter, "had convergence problems!")
}
mixEstPar[c(2,3),i] <- Lest$par
mui <- inv_logit( Lest$par[1])
nmui <- inv_logit(-Lest$par[1])
ni <- exp(Lest$par[2])
mixEst[2,i] <- mui * ni
mixEst[3,i] <- nmui * ni
}
mixEstPar[1,] <- logit(mixEst[1,,drop=FALSE])
ind <- 1 + iter %% Neps
runMixPar[ind,,] <- mixEstPar
runOrder[ind] <- iter
iter <- iter + 1
}
if(iter == Niter.max)
warning("Maximum number of iterations reached.")
mixEst <- mixEst[,order(mixEst[1,], decreasing=TRUE),drop=FALSE]
colnames(mixEst) <- paste("comp", seq(Nc), sep="")
dlink(mixEst) <- identity_dlink
class(mixEst) <- c("EM", "EMbmm", "betaMix", "mix")
## give further details
attr(mixEst, "df") <- Nc-1 + 2*Nc
attr(mixEst, "nobs") <- N
attr(mixEst, "lli") <- lliCur
attr(mixEst, "Nc") <- Nc
attr(mixEst, "tol") <- tol
attr(mixEst, "traceLli") <- traceLli
attr(mixEst, "traceMix") <- lapply(traceMix, function(x) {
class(x) <- c("betaMix", "mix")
x})
attr(mixEst, "x") <- x
mixEst
}
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.