###################################################
### chunk number 1:
###################################################
# lag - which lag for observation-driven part?
# y_i,t = lambda*y_i,t-lag NOTE: lag=-1 means y_i,t+1
# lag.range =c(lag.neg, lag.pos)
# i.e. (1,0) for t-1,t (DEFAULT)
# (2,2) for t-2,t-1,t,t+1,t+2
algo.hhh <- function(disProgObj,
control=list(lambda=TRUE,
neighbours=FALSE,
linear=FALSE,
nseason=0,
negbin=c("none", "single", "multiple"),
proportion=c("none", "single", "multiple"),
lag.range=NULL),
thetastart=NULL,
verbose=TRUE){
#Convert sts objects
if (class(disProgObj) == "sts") disProgObj <- sts2disProg(disProgObj)
#set default values (if not provided in control)
if(is.null(control[["linear",exact=TRUE]]))
control$linear <- FALSE
if(is.null(control[["nseason",exact=TRUE]]))
control$nseason <- 0
if(is.null(control[["neighbours",exact=TRUE]]))
control$neighbours <- NA
if(is.null(control[["negbin",exact=TRUE]]))
control$negbin <- "none"
if(is.null(control[["lambda",exact=TRUE]]))
control$lambda <- 1
if(is.null(control[["proportion",exact=TRUE]]))
control$proportion <- "none"
control$negbin <- match.arg(control$negbin, c("single","multiple","none"))
control$proportion <- match.arg(control$proportion, c("single","multiple","none"))
# convert logical values to numerical values, FALSE corresponds to NA
# to allow for lag == 0
if(is.logical(control[["lambda", exact=TRUE]])){
control$lambda <- as.numeric(control$lambda)
control$lambda[control$lambda==0] <- NA
}
if(is.logical(control[["neighbours", exact=TRUE]])){
control$neighbours <- as.numeric(control$neighbours)
control$neighbours[control$neighbours==0] <- NA
}
# determine range of observations y_i,t
if(is.null(control[["lag.range",exact=TRUE]])){
lags <- c(control$lambda, control$neighbours)
control$lag.range <- c(max(c(lags,1),na.rm=TRUE),
max(c(-lags,0), na.rm=TRUE))
}
# check if observed is a vector and convert to matrix if necessary
if(is.vector(disProgObj$observed)) disProgObj$observed <- as.matrix(disProgObj$observed)
n <- nrow(disProgObj$observed)
nareas <- ncol(disProgObj$observed)
#univariate
if(nareas ==1){
control$neighbours <- NA
control$proportion <- "none"
control$nseason <- control$nseason[1]
}
# model with (lambda, pi) ?
if(control$proportion != "none"){
control$neighbours <- NA
# no lambda specified or lambda not specified for each area
if(sum(!is.na(control$lambda)) == 0 | sum(!is.na(control$lambda))!= nareas)
control$lambda <- 1
}
# check neighbourhood matrix if neighbours=TRUE or proportion!="none"
if(sum(!is.na(control$neighbours))>0 | control$proportion != "none"){
# is there a neighbourhood matrix?
if(is.null(disProgObj$neighbourhood)) stop("No neighbourhood matrix is provided\n")
if(any(is.na(disProgObj$neighbourhood))) stop("No correct neighbourhood matrix given\n")
}
#make "design" matrices
designRes<- make.design(disProgObj=disProgObj, control=control)
# check if there are neighbours
if(designRes$dim$phi > 0){
nOfNeighbours <- designRes$nOfNeighbours
if((designRes$dim$phi==1) & (sum(nOfNeighbours)==0))
stop("Specified model is not in line with neighbourhood matrix\n")
# if((designRes$dim$phi==nareas) & (any(nOfNeighbours[!is.na(control$neighbours)]==0)))
if((length(control$neighbours) == nareas) & (any(nOfNeighbours[!is.na(control$neighbours)]==0)))
stop("Specified model is not in line with neighbourhood matrix\n")
} else if(designRes$dim$proportion > 0){
nOfNeighbours <- designRes$nOfNeighbours
if((designRes$dim$proportion==1) & (sum(nOfNeighbours)==0))
stop("Specified model is not in line with neighbourhood matrix\n")
if((designRes$dim$proportion==nareas) & (any(nOfNeighbours==0)))
stop("Specified model is not in line with neighbourhood matrix\n")
}
dimtheta <- designRes$dimTheta$dim
dimLambda <- designRes$dimTheta$lambda
dimPhi <- designRes$dimTheta$phi
#starting values for optim
areastart <- log(colMeans(designRes$Y)/designRes$populationFrac[1,])
if(!is.null(thetastart)){
#check dimension of thetastart
# must be either of length dimtheta or of length dimtheta-nareas
if(all(length(thetastart) != c(dimtheta, dimtheta-nareas)) ){
cat('thetastart must be of length', dimtheta, 'or ',dimtheta-nareas,'\n')
return(NULL)
}
theta <- thetastart
if(length(theta) == dimtheta) areastart <- NULL
} else {
#set starting values for theta
#lambda = log(0.5), phi = log(0.1), beta = gamma = delta = 0, psi = 1
theta <- c(rep(log(0.5),designRes$dimTheta$lambda), rep(log(0.1),designRes$dimTheta$phi),
rep(0.5,designRes$dimTheta$proportion),
rep(0, designRes$dimTheta$trend + designRes$dimTheta$season), rep(2,designRes$dimTheta$negbin) )
}
#starting values for intercepts
if(!is.null(areastart)){
if(dimLambda + dimPhi >0){
#cat("theta",theta[1:(dimLambda + dimPhi)],"\n")
Lambda <- getLambda(theta[1:(dimLambda + dimPhi)], designRes)
expAlpha <- expAlpha.mm(Lambda,designRes$Y)
expAlpha[expAlpha <=0] <- (colMeans(designRes$Y)/designRes$populationFrac[1,])[expAlpha <=0]
areastart <- log(expAlpha)
#areastart <- log(expAlpha.mm(Lambda,designRes$Y))
}
theta <- c(areastart,theta)
#cat("initial values",theta,"\n")
}
#check if initial values are valid
mu<-meanResponse(theta,designRes)$mean
if(any(mu==0) | any(!is.finite(mu)))
stop("invalid initial values\n")
# maximize loglikelihood
mycontrol <- list(fnscale=-1, type=3, maxit=1000)
suppressWarnings(myoptim <- optim(theta, fn=loglikelihood, gr=gradient,
control=mycontrol, method="BFGS", hessian=TRUE, designRes=designRes))
if(myoptim$convergence==0){
convergence <- TRUE
} else {
if(verbose)
cat("Algorithm has NOT converged. \n")
res <- list(convergence=FALSE)
class(res) <- "ah"
return(res)
}
loglik <- myoptim$value
if(loglik==0){
if(verbose){
cat('loglikelihood = 0\n')
cat('Results are not reliable! Try different starting values. \n')
}
res <- list(convergence=FALSE)
class(res) <- "ah"
return(res)
}
thetahat <- myoptim$par
fisher <- -myoptim$hessian
# fitted values
fitted <- meanResponse(thetahat,designRes)$mean
#psi, lambda and phi are on log-scale
#-> transformation of estimates, standard errors and fisher (using delta rule)
#labels for results
D <- jacobian(thetahat, designRes)$D
thetahat <- jacobian(thetahat, designRes)$theta
#Approximation to the inverted fisher info matrix
# cov <- try(D %*% solve(fisher) %*% D, silent=TRUE)
cov <- try(D %*% solve(fisher) %*% t(D), silent=TRUE)
#fisher info is singular
if(class(cov) == "try-error"){
if(verbose){
cat("Fisher info singular \t loglik=",loglik," \n")
cat("theta",round(thetahat,2),"\n")
cat('Results are not reliable! Try different starting values. \n')
}
res <- list(convergence=FALSE)
class(res) <- "ah"
return(res)
}
if(any(!is.finite(diag(cov))) | any(diag(cov)<0)){
if(verbose){
cat("infinite or negative cov\t loglik=",loglik,"\n")
cat("theta",round(thetahat,2),"\n")
cat('Results are not reliable! Try different starting values. \n')
}
res <- list(convergence=FALSE)
class(res) <- "ah"
return(res)
}
se <- sqrt(diag(cov))
if(convergence & verbose)
cat("Algorithm claims to have converged \n")
result <- list(coefficients=thetahat, se=se, cov=cov, call=match.call(),
loglikelihood=loglik, convergence=convergence,
fitted.values=fitted, control=control,disProgObj=disProgObj,
lag=designRes$lag, nObs=designRes$nObs)
class(result) <- "ah"
return(result)
}
###################################################
### chunk number 2:
###################################################
algo.hhh.grid <- function(disProgObj, control=list(lambda=TRUE,neighbours=FALSE,
linear=FALSE, nseason=0,
negbin=c("none", "single", "multiple"),
proportion=c("none", "single", "multiple"),lag.range=NULL),
thetastartMatrix, maxTime=1800, verbose=FALSE){
#convert disProgObj if necessary
if (class(disProgObj) == "sts") disProgObj <- sts2disProg(disProgObj)
#set default values (if not provided in control)
if(is.null(control[["linear",exact=TRUE]]))
control$linear <- FALSE
if(is.null(control[["nseason",exact=TRUE]]))
control$nseason <- 0
if(is.null(control[["neighbours",exact=TRUE]]))
control$neighbours <- NA
if(is.null(control[["negbin",exact=TRUE]]))
control$negbin <- "none"
if(is.null(control[["lambda",exact=TRUE]]))
control$lambda <- 1
if(is.null(control[["proportion",exact=TRUE]]))
control$proportion <- "none"
control$negbin <- match.arg(control$negbin, c("single","multiple","none"))
control$proportion <- match.arg(control$proportion, c("single","multiple","none"))
# convert logical values to numerical values, FALSE corresponds to NA
# to allow for lag == 0
if(is.logical(control[["lambda", exact=TRUE]])){
control$lambda <- as.numeric(control$lambda)
control$lambda[control$lambda==0] <- NA
}
if(is.logical(control[["neighbours", exact=TRUE]])){
control$neighbours <- as.numeric(control$neighbours)
control$neighbours[control$neighbours==0] <- NA
}
# determine range of observations y_i,t
if(is.null(control[["lag.range",exact=TRUE]])){
lags <- c(control$lambda, control$neighbours)
control$lag.range <- c(max(c(lags,1),na.rm=TRUE),
max(c(-lags,0), na.rm=TRUE))
}
n <- nrow(disProgObj$observed)
nareas <- ncol(disProgObj$observed)
# check parameter specification for season
#univariate
if(nareas ==1){
control$neighbours <- NA
control$proportion <- "none"
control$nseason <- control$nseason[1]
}
# model with (lambda, pi) ?
if(control$proportion != "none"){
control$neighbours <- NA
# no lambda specified or lambda not specified for each area
if(sum(!is.na(control$lambda)) == 0 | sum(!is.na(control$lambda))!= nareas)
control$lambda <- 1
}
# check neighbourhood matrix if neighbours=TRUE or proportion!="none"
if(sum(!is.na(control$neighbours))>0 | control$proportion != "none"){
if(any(is.na(disProgObj$neighbourhood)))
stop("No correct neighbourhood matrix given\n")
}
designRes<- make.design(disProgObj=disProgObj, control=control)
# check if there are neighbours
if(designRes$dim$phi > 0){
nOfNeighbours <- designRes$nOfNeighbours
if((designRes$dim$phi==1) & (sum(nOfNeighbours)==0))
stop("Specified model is not in line with neighbourhood matrix\n")
# if((designRes$dim$phi==nareas) & (any(nOfNeighbours[!is.na(control$neighbours)]==0)))
if((length(control$neighbours) == nareas) & (any(nOfNeighbours[!is.na(control$neighbours)]==0)))
stop("Specified model is not in line with neighbourhood matrix\n")
} else if(designRes$dim$proportion > 0){
nOfNeighbours <- designRes$nOfNeighbours
if((designRes$dim$proportion==1) & (sum(nOfNeighbours)==0))
stop("Specified model is not in line with neighbourhood matrix\n")
if((designRes$dim$proportion==nareas) & (any(nOfNeighbours==0)))
stop("Specified model is not in line with neighbourhood matrix\n")
}
dimthetaStart <- designRes$dimTheta$dim -nareas
if(dimthetaStart == 0){ #only intercepts, grid search not necessary
return(algo.hhh(disProgObj=disProgObj,control=control))
}
#check dimension of thetastartMatrix
if(!is.matrix(thetastartMatrix)){
cat('thetastart must be a matrix with', designRes$dimTheta$dim, 'or ', dimthetaStart, 'columns\n')
return(NULL)
}
if(all(ncol(thetastartMatrix) != c(designRes$dimTheta$dim, dimthetaStart))){
cat('thetastart must be a matrix with', designRes$dimTheta$dim, 'or ', dimthetaStart,'columns\n')
return(NULL)
}
#try multiple starting values and return the result with highest likelihood
#stop search once time limit is exceeded
i<-0
nOfIter <- nrow(thetastartMatrix)
gridUsed <- nOfIter
if(verbose) cat('The size of grid is', nOfIter, '\n')
bestLoglik <- list(loglikelihood = -1e99)
allLoglik <- matrix(NA,nrow=nOfIter,ncol=designRes$dimTheta$dim+1)
time <- maxTime
while((time > 0) & (i < nOfIter)){
i <- i+1
#run algo.hhh with the i-th row of thetastartMatrix as initial values
time.i <- system.time(res<-try(algo.hhh(disProgObj=disProgObj,control=control,thetastart=thetastartMatrix[i,],verbose=verbose),silent=!verbose))[3]
#how much time is left now
time <- time - time.i
#print progress information
if(verbose){
if(class(res)== "try-error")
print(c(niter=i,timeLeft=time,loglik=NULL))
else print(c(niter=i,timeLeft=time,loglik=res$loglikelihood))
}
#don't consider "useless" results for the search of the best loglikelihood
if(class(res)!= "try-error" && res$convergence){
#save loglik
allLoglik[i,] <- c(res$loglikelihood,coef(res))
#keep it as bestLoglik if loglikelihood improved
if(res$loglikelihood > bestLoglik$loglikelihood){
bestLoglik <- res
}
}
}
if(time < 0){
if(verbose) cat('Time limit exceeded, grid search stopped after', i, 'iterations. \n')
allLoglik <- as.matrix(allLoglik[1:i,])
gridUsed <- i
}
timeUsed <- ifelse(time>0, maxTime-time,maxTime+abs(time))
#algo.hhh did not converge or produced useless results for all starting values,
#i.e. there is no result
if(is.null(coef(bestLoglik))) {
#convergence <- FALSE
#cat('Algorithms did not converge, please try different starting values! \n')
bestLoglik <- list(loglikelihood=NULL,convergence=FALSE)
} else{
#give names to all Loglik-matrix
colnames(allLoglik) <- c("loglik",names(coef(bestLoglik)))
}
result <- list(best = bestLoglik, allLoglik = allLoglik,gridSize=nOfIter,gridUsed=gridUsed, time=timeUsed,convergence=bestLoglik$convergence)
class(result) <- "ahg"
return(result)
}
###################################################
### chunk number 3:
###################################################
create.grid <- function(disProgObj, control, params = list(epidemic = c(0.1, 0.9, 5),
endemic=c(-0.5,0.5,3), negbin = c(0.3, 12, 10))) {
#convert S4 sts to S3 if necessary
if (class(disProgObj) == "sts") disProgObj <- sts2disProg(disProgObj)
designRes <- make.design(disProgObj, control)
control <- designRes$control
dimParams <- designRes$dimTheta
dimLambda <- dimParams$lambda
dimPhi <- dimParams$phi
dimProp <- dimParams$proportion
dimEndemic <- dimParams$trend + dimParams$season
dimNegbin <- dimParams$negbin
# check if initial values are provided
if((dimLambda +dimPhi > 0) & is.null(params$epidemic))
stop("Please provide initial values for the epidemic component \n")
if((dimEndemic > 0) & is.null(params$endemic))
stop("Please provide initial values for the endemic component \n")
if((dimNegbin > 0) & is.null(params$negbin))
stop("Please provide initial values for the dispersion parameter psi \n")
# check if initial values are specified correctly
if(!is.null(params$epidemic)){
if( params$epidemic[3]%%1 !=0 | params$epidemic[3]<1 | sign(params$epidemic[3])== -1)
stop("Last component of params$epidemic must be a positive integer\n")
}
if(!is.null(params$endemic)){
if( params$endemic[3]%%1 !=0 | params$endemic[3]<1 | sign(params$endemic[3])== -1)
stop("Last component of params$endemic must be a positive integer\n")
}
if(!is.null(params$negbin)){
if( params$negbin[3]%%1 !=0 | params$negbin[3]<1 | sign(params$negbin[3])== -1)
stop("Last component of params$negbin must be a positive integer\n")
}
grid <- list()
if(dimNegbin >0){
psi <- seq(params$negbin[1], params$negbin[2], length = params$negbin[3])
if(any(psi<=0))
stop("Initial values for psi must be positive\n")
log.psi <- log(psi[psi >0])
grid$psi <- log.psi
}
if(dimLambda >0){
epidemic <- seq(params$epidemic[1], params$epidemic[2], length = params$epidemic[3])
if(any(epidemic<=0))
stop("Iinitial values for lambda must be positive\n")
log.lambda <- log(epidemic[epidemic >0])
grid$lambda <- log.lambda
}
if(dimPhi >0){
epidemic <- seq(params$epidemic[1], params$epidemic[2], length = params$epidemic[3])
if(any(epidemic<=0))
stop("Initial values for phi must be positive\n")
log.lambda <- log(epidemic[epidemic >0])
grid$phi <- log.lambda
}
if(dimProp >0){
if(any(epidemic<=0 | epidemic >=1))
stop("initial values for pi must be in (0,1)\n")
logit.prop <- log(epidemic[epidemic > 0 & epidemic < 1]) - log(1-epidemic[epidemic > 0 & epidemic < 1])
grid$prop <- logit.prop
}
if(dimEndemic >0){
endemic <- seq(params$endemic[1], params$endemic[2], length = params$endemic[3])
grid$endemic <- endemic
}
grid <- expand.grid(grid)
grid <- as.matrix( grid[c(rep("lambda",dimLambda), rep("phi",dimPhi), rep("prop",dimProp),
rep("endemic",dimEndemic), rep("psi",dimNegbin))] )
gridSize <- nrow(grid)
cat("Matrix with ",gridSize, " initial values\n")
return(grid)
}
###################################################
### chunk number 4:
###################################################
loglikelihood <- function(theta, designRes){
control <- designRes$control
Y <- designRes$Y
mean <- meanResponse(theta=theta, designRes=designRes)$mean
dimNegbin <- designRes$dimTheta$negbin
dimTheta <- designRes$dimTheta$dim
#loglikelihood poisson
if(dimNegbin==0){
result <- colSums(dpois(Y, lambda=mean, log=TRUE))
} else if(dimNegbin==1){
#loglikelihood negbin
#ensure psi (on last position in vector theta) ist positive
psi <- exp(theta[dimTheta])
result <- colSums(dnbinom(Y, size=psi, mu=mean, log=TRUE))
} else if(dimNegbin>1){
#loglikelihood negbin, multiple dispersion params
#ensure psi (on last positions) is positive
psi <- exp(theta[(dimTheta-dimNegbin+1):dimTheta])
psi <- matrix(psi,ncol=dimNegbin, nrow=nrow(Y), byrow=TRUE)
result <- colSums(dnbinom(Y, size=psi, mu=mean, log=TRUE))
}
res <- sum(result)
attr(res, "colsums") <- result
return(res)
}
###################################################
### chunk number 5:
###################################################
meanResponse <- function(theta, designRes){
# unpack design matrices
Y <- designRes$Y
nareas <- ncol(Y)
n <- nrow(Y)
X.trendSeason <- designRes$X.trendSeason
Ym1 <- designRes$Ym1
Ym1.neighbours <- designRes$Ym1.neighbours
nhood <- designRes$disProgObj$neighbourhood
nOfNeighbours <- designRes$nOfNeighbours
pop <- designRes$populationFrac
#check dimension of theta
if(designRes$dimTheta$dim != length(theta)){
cat('theta must be of length',designRes$dimTheta$dim,'\n')
return(NULL)
}
#unpack parameters and ensure lambda and phi are positive
params <- unpackParams(theta,designRes)
###################################################################
## calculation of mean
#autoregressive part
# model with lambda and phi ?
if(designRes$control$proportion == "none"){
#auto=0 if lambda and phi are not used in model
lambda <- params$lambda
phi <- params$phi
# no autoregression
if(is.null(lambda))
auto.lambda<- 0
else {
# multiple lambda
if(length(designRes$control$lambda)==nareas){
# create vector lambda with elements 0 if control$lambda=FALSE
lambda <- rep(0,nareas)
lambda[!is.na(designRes$control$lambda)] <- params$lambda
}
auto.lambda <- Ym1*matrix(lambda,ncol=nareas,nrow=nrow(Y), byrow=TRUE)
}
if(is.null(phi))
auto.phi <- 0
else {
# multiple phi
if(length(designRes$control$neighbours)==nareas){
# create vector phi with elements 0 if control$neighbours=FALSE
phi <- rep(0,nareas)
phi[!is.na(designRes$control$neighbours)] <- params$phi
}
auto.phi <- Ym1.neighbours*matrix(phi,ncol=nareas,nrow=nrow(Y), byrow=TRUE)
}
auto<- auto.lambda + auto.phi
} else {
#################################################
## model with lambda and proportion pi
#################################################
# helper function
weightedSumEpidemic <- function(prop,lambda){
# ensure region id is not included
diag(nhood) <- 0
# compute sum_j~i {pi_ji * Y_j,t-1} for unit id
# where pi_ji = pi_i for j=i
# pi_ji =(1-pi_j)/|k~j| for j~i
one <- function(id){
# nOfNeighbours is number of Neigbours for each unit id=1,..,m i.e. |k~id|
nOfNeighbours[id]<-1
pi.ij <- matrix(lambda*(1-prop)/nOfNeighbours,ncol=length(prop),nrow=nrow(Ym1),byrow=TRUE)
# select pi_ij with j~i
piYm1 <- as.matrix((Ym1*pi.ij)[,nhood[,id]>0])
rowSums(piYm1)
}
sapply(1:length(prop),one)
}
lambda <- matrix(params$lambda,ncol=nareas,nrow=n,byrow=TRUE)
if(designRes$control$proportion == "single")
prop <- rep(params$pi,nareas)
else prop <- params$pi
# lambda*pi_ji*Y_j,t-1
auto.phi <- weightedSumEpidemic(prop=prop,lambda=lambda[1,])
auto.lambda <- Ym1*lambda*matrix(prop,ncol=nareas,nrow=n,byrow=TRUE)
auto <- auto.lambda+auto.phi
}
################
#trend and seasonal components
nSeason <- designRes$control$nseason
dimSeason <- designRes$dimTheta$season
dimTrend <- designRes$dimTheta$trend
# trend
if(dimTrend >0){
if(length(designRes$control$linear) == 1)
beta <- rep(params$beta,nareas)
else {
beta <- rep(0,nareas)
beta[designRes$control$linear] <-params$beta
}
predTime <- as.matrix(X.trendSeason[,1])%*%beta
} else predTime <- 0
# season
if( dimSeason >0){
# discard design matrix for trend
X.Season <- X.trendSeason[,(1+ (dimTrend>0) ):ncol(X.trendSeason)]
maxSeason <- max(nSeason)
#construct a suitable matrix for seasonal parameters gamma_i
# same number of Fourier frequencies S for all areas i:
if(length(nSeason)==1){
gammaMat <- matrix(params$gamma,ncol=nareas,nrow=2*maxSeason,byrow=FALSE)
} else if(length(nSeason)==nareas){
# different number of frequencies S_i for each area
gammaMat <- matrix(0,ncol=nareas,nrow=2*maxSeason)
index <- rep(1:nareas,2*nSeason)
for(i in 1:nareas)
gammaMat[0:(2*nSeason[i]),i] <- params$gamma[index==i]
} else stop("nseason must be a vector of length 1 or",nareas,"\n")
predSeason <- X.Season%*%gammaMat
} else predSeason <- 0
#intercepts for areas
#matrix with columns (alpha_1,...,alpha_nareas)
predarea <- matrix(params$alpha, byrow=TRUE, ncol=nareas, nrow=nrow(Y))
#endemic part
endemic <- pop*exp(predarea+predTime+predSeason)
#results
mean <- auto + endemic
#Done
return(list(mean=mean,epidemic=auto,endemic=endemic,epi.own=auto.lambda,epi.neighbours=auto.phi))
}
###################################################
### chunk number 6:
###################################################
make.design <- function(disProgObj, control=list(lambda=TRUE, neighbours=FALSE,
linear=FALSE, nseason=0,
negbin=c("none", "single", "multiple"),
proportion=c("none", "single", "multiple"),
lag.range=NULL) ){
#Convert sts objects
if (class(disProgObj) == "sts") disProgObj <- sts2disProg(disProgObj)
#set default values (if not provided in control)
if(is.null(control[["linear",exact=TRUE]]))
control$linear <- FALSE
if(is.null(control[["nseason",exact=TRUE]]))
control$nseason <- 0
if(is.null(control[["neighbours",exact=TRUE]]))
control$neighbours <- NA
if(is.null(control[["negbin",exact=TRUE]]))
control$negbin <- "none"
if(is.null(control[["lambda",exact=TRUE]]))
control$lambda <- 1
if(is.null(control[["proportion",exact=TRUE]]))
control$proportion <- "none"
control$proportion <- match.arg(control$proportion, c("single","multiple","none"))
control$negbin <- match.arg(control$negbin, c("single","multiple","none"))
# convert logical values to numerical values, FALSE corresponds to NA
# to allow for lag == 0
if(is.logical(control[["lambda", exact=TRUE]])){
control$lambda <- as.numeric(control$lambda)
control$lambda[control$lambda==0] <- NA
}
if(is.logical(control[["neighbours", exact=TRUE]])){
control$neighbours <- as.numeric(control$neighbours)
control$neighbours[control$neighbours==0] <- NA
}
# determine range of observations y_i,t
if(is.null(control[["lag.range",exact=TRUE]])){
lags <- c(control$lambda, control$neighbours)
control$lag.range <- c(max(c(lags,1),na.rm=TRUE),
max(c(-lags,0), na.rm=TRUE))
}
data <- disProgObj$observed
n <- nrow(data)
nareas <- ncol(data)
# check parameters
if(length(control$lambda)>1 & length(control$lambda)!=nareas)
stop("parameter lambda is not specified correctly\n")
if(length(control$neighbours)>1 & length(control$neighbours)!=nareas)
stop("parameter phi is not specified correctly\n")
if(length(control$linear)>1 & length(control$linear)!=nareas)
stop("parameter beta is not specified correctly\n")
#univariate
if(nareas ==1){
control$neighbours <- NA
control$proportion <- "none"
control$nseason <- control$nseason[1]
}
# maximum number of seasonal Fourier frequencies
maxSeason <- max(control$nseason)
# model with (lambda, pi) ?
if(control$proportion != "none"){
control$neighbours <- NA
# no lambda specified or lambda is not specified for each area
if(sum(!is.na(control$lambda)) == 0 |sum(!is.na(control$lambda)) !=nareas)
control$lambda <- 1
}
dimLambda <- sum(!is.na(control$lambda))
dimPhi <- sum(!is.na(control$neighbours))
dimProportion <- switch(control$proportion ,
"single" = 1,
"multiple"= nareas,
"none" = 0)
dimTrend <- sum(control$linear)
dimSeason <- sum(2*control$nseason)
dimIntercept <- nareas
dimNegbin <- switch(control$negbin,
"single" = 1,
"multiple"= nareas,
"none" = 0)
#theta = (alpha_i, lambda, phi (or pi), beta,gamma_i,delta_i,..., psi)
dim <- dimLambda+dimPhi+dimTrend+dimSeason+dimIntercept+dimProportion+dimNegbin
dimTheta <- list(lambda=dimLambda, phi=dimPhi, trend=dimTrend, season=dimSeason,
intercept=dimIntercept, proportion=dimProportion, negbin=dimNegbin ,dim=dim)
####################################################################
# arrange response as matrix
#Y, Ym1, Ym1.neighbours and population are (nOfobs)x(nOfareas) matrices
#where nOfareas is the number of areas/units and
# nOfobs is determined by control$lag.range with default nOfObs=n-1
# Thus, lag.range can be used to ensure that models with different lags
# are based on the same observations.
t.min <- 1+control$lag.range[1]
t.max <- n-control$lag.range[2]
Y <- matrix(data[t.min:t.max,],nrow=length(t.min:t.max),ncol=nareas)
# population sizes n_{i,t}
if(is.null(disProgObj$populationFrac)){
population <- matrix(1, nrow=length(t.min:t.max),ncol=nareas)
} else {
population <- matrix(disProgObj$populationFrac[t.min:t.max,],nrow=length(t.min:t.max),ncol=nareas)
}
# observed counts at time point t-lag
# NOTE: the same lag (the maximum lag) is used for all areas
if(dimLambda >0){
lag.lambda <- control$lambda[which.max(abs(control$lambda))]
Ym1 <- matrix(data[(t.min:t.max)-lag.lambda,],nrow=length(t.min:t.max),ncol=nareas)
} else {
lag.lambda<- NA
Ym1 <- matrix(0,nrow=length(t.min:t.max),ncol=nareas)
}
Ym1.neighbours <- matrix(0,nrow=length(t.min:t.max),ncol=nareas)
nOfNeighbours <- 0
# now matrix for neighbours
if(dimPhi>0){
lag.phi <- control$neighbours[which.max(abs(control$neighbours))]
Ym1.neighbours <- weightedSumNeighbours(disProgObj)$neighbours[(t.min:t.max)-lag.phi,]
nOfNeighbours <- weightedSumNeighbours(disProgObj)$nOfNeighbours
# Ym1.neighbours <- sumNeighbours(disProgObj)[-n,]
} else lag.phi <- NA
if(dimProportion >0){
Ym1.neighbours <- weightedSumNeighbours(disProgObj)$neighbours[(t.min:t.max)-lag.lambda,] #not really needed
nOfNeighbours <- weightedSumNeighbours(disProgObj)$nOfNeighbours
}
####################################################################
# now define design matrix (for trend and seasonality) for each time point
#t<- disProgObj$week[t.min:t.max]
# if no $week is given
if(is.null(disProgObj$week)){
t <- (t.min:t.max)-1
} else {
t<- disProgObj$week[(t.min:t.max)-1]
}
#t <- t - mean(t)
form<-function(mod=ifelse(dimTrend == 0,"~-1","~-1+t"),
S=maxSeason, period=disProgObj$freq){
if(S>0){
for(i in 1:S){
mod <- paste(mod,"+sin(",2*i,"*pi*t/",period,")+cos(",2*i,"*pi*t/",period,")",sep="")
}
}
return(as.formula(mod))
}
if(dimTrend +dimSeason >0)
X.trendSeason<-model.matrix(form(),data.frame(t=t))
else X.trendSeason <-NULL
result <- list("Y"=Y, "Ym1"=Ym1, "Ym1.neighbours"=Ym1.neighbours,"nOfNeighbours"=nOfNeighbours,
"X.trendSeason"=X.trendSeason,
"populationFrac"=population, "dimTheta"=dimTheta,
"control"=control,"disProgObj"=disProgObj, "lag"=c(lag.lambda,lag.phi),"nObs"=prod(dim(Ym1)))
return(result)
}
###################################################
### chunk number 7:
###################################################
print.ah <- function(x,digits = max(3, getOption("digits") - 3), amplitudeShift=TRUE,reparamPsi=TRUE,...){
if(!x$convergence)
cat('Results are not reliable! Try different starting values. \n')
else {
if(!is.null(x$call)){
cat("Call: \n")
print(x$call)
}
cat('\nEstimated parameters and standard errors: \n\n')
coefs <- coefficients(x, se=TRUE, amplitudeShift=amplitudeShift,reparamPsi=reparamPsi)
print(round(cbind("Estimates"=coefs[,"Estimates"],
"Std.Error"=coefs[,"Std. Error"]),digits=digits),print.gap=2)
cat('\nlog-likelihood: ',round(x$loglik,digits=digits-2),'\n')
cat('AIC: ',round(AIC(x),digits=digits-2),'\n')
cat('BIC: ',round(AIC(x,k=log(x$nObs)),digits=digits-2),'\n\n')
if(!is.na(x$lag[1])) cat('lag used for lambda: ',x$lag[1],'\n')
if(!is.na(x$lag[2])) cat('lag used for phi: ',x$lag[2] ,'\n')
cat('number of observations: ',x$nObs,'\n\n')
}
}
print.ahg <- function (x, digits = max(3, getOption("digits") - 3), amplitudeShift=TRUE,reparamPsi=TRUE, ...){
cat("\nsize of grid: ", x$gridSize, "\n")
if (x$gridSize != x$gridUsed)
cat("grid search stopped after", x$gridUsed, "iterations \n")
cat("convergences: ",sum(!is.na(x$all[,1])),"\n")
cat("time needed (in seconds)",x$time,"\n\n")
if (!x$convergence)
cat("\nAlgorithms did not converge, please try different starting values! \n")
else {
x$best$call <- NULL
cat("values of log-likelihood:")
print(table(round(x$all[,1],0)))
# cat("\n")
print.ah(x$best, digits = digits, amplitudeShift=amplitudeShift,reparamPsi=reparamPsi)
}
}
###################################################
### chunk number 8:
###################################################
#################################
# obtain predictions from the fitted algo.hhh model
#
# params:
# object - a fitted object of class "ah"
# newdata - optionally, a disProgObject with which to predict;
# if omitted, the fitted mean is returned.
# type - the type of prediction required. The default is on the scale of the response
# variable (endemic and epidemic part)
# the alternative "endemic" returns only the endemic part (i.e. n_it * \nu_it)
################################
predict.ah <- function(object,newdata=NULL,type=c("response","endemic","epi.own","epi.neighbours"),...){
type <- match.arg(type,c("response","endemic","epi.own","epi.neighbours"))
control <- object$control
if(is.null(newdata))
newdata <- object$disProgObj
if(!inherits(newdata, "disProg"))
stop("data must be an object of class disProg\n")
coefs <- coefficients(object)
design <- make.design(newdata,control=control)
# in meanResponse the params lambda, phi are "exp()'ed"
# log() them to obtain the correct predictions
if(sum(!is.na(control$lambda)) >0 | sum(!is.na(control$neighbours)) >0){
indexL <- design$dimTheta$intercept+1
indexU <- indexL +design$dimTheta$lambda +design$dimTheta$phi -1
coefs[indexL:indexU] <- log(coefs[indexL:indexU])
#cat("lambda,phi: indexL",indexL,"indexU",indexU,"\n")
# pi is on logit-scale
if(control$proportion != "none"){
indexL <- design$dimTheta$intercept+design$dimTheta$lambda+1
indexU <- indexL +design$dimTheta$proportion -1
#cat("indexL",indexL,"indexU",indexU,"\n")
coefs[indexL:indexU] <- log(coefs[indexL:indexU]/(1-coefs[indexL:indexU]))
}
}
predicted <- meanResponse(coefs,design)
if(type=="response")
return(predicted$mean)
else if(type=="endemic") return(predicted$endemic)
else if(type=="epi.own") return(predicted$epi.own)
else if(type=="epi.neighbours") return(predicted$epi.neighbours)
}
predict.ahg <- function(object, newdata=NULL, type=c("response","endemic","epi.own","epi.neighbours"),...){
predict(object$best,newdata=newdata,type=type)
}
###################################################
### chunk number 9:
###################################################
##########################
## residuals
##################
residuals.ah <- function (object, type = c("deviance", "pearson"), ...){
type <- match.arg(type, c("deviance", "pearson"))
# fitted values
mean<- object$fitted.values
#discard 1st observation (to obtain same dimension as mean)
y <- as.matrix(object$disProgObj$observed[-1,])
# poisson or negbin model
if(object$control$negbin!="none"){
coefs <- coefficients(object)
psi <- matrix(coefs[grep("psi",names(coefs))],ncol=ncol(y),nrow=nrow(y),byrow=TRUE)
distr <- function(mu){ dnbinom(y, mu=mu, size=psi, log=TRUE) }
variance <- mean*(1+mean/psi)
} else {
distr <- function(mu){ dpois(y, lambda=mu,log=TRUE) }
variance <- mean
}
res <- switch(type,
deviance = sign(y-mean)*sqrt(2*(distr(y)-distr(mean))),
pearson = (y-mean)/sqrt(variance)
)
return(res)
}
residuals.ahg <- function(object, type = c("deviance", "pearson"), ...){
residuals.ah(object$best,type=type)
}
###################################################
### chunk number 10:
###################################################
############################################
# extract estimates and standard errors (se=TRUE)
# if amplitudeShift=TRUE, the seasonal params are transformed
# if reparamPsi=TRUE, the overdispersion param psi is transformed to 1/psi
#
############################################
coef.ah <- function(object,se=FALSE, amplitudeShift=FALSE, reparamPsi=FALSE,...){
coefs <- object$coefficients
stdErr <- object$se
if(amplitudeShift & max(object$control$nseason)>0){
#extract sin, cos coefficients
index <- grep(" pi ",names(coefs))
sinCos.names <- names(coefs)[index]
# change labels
names(coefs)[index] <- paste(c("A","s"),substr(sinCos.names,4,100),sep="")
#transform sin, cos coefficients
coefs[index] <- sinCos2amplitudeShift(coefs[index])
# se's using Delta rule
D <- diag(1,length(coefs))
D[index,index]<- jacobianAmplitudeShift(coefs[index])
cov <- D %*% object$cov %*% t(D)
stdErr <- sqrt(diag(cov))
}
if(reparamPsi & object$control$negbin!="none"){
#extract psi coefficients
index <- grep("psi",names(coefs))
psi.names <- names(coefs)[index]
# change labels
names(coefs)[index] <- paste("1/",psi.names,sep="")
#transform psi coefficients
coefs[index] <- 1/coefs[index]
# se's using Delta rule: se[h(psi)] = se[psi] * |h'(psi)|
# h = 1/psi, h' = -1/psi^2
D <- diag(coefs[index]^2,length(index))
stdErr[index] <- sqrt(diag(D %*% object$cov[index,index] %*% t(D)))
}
if(se)
return(cbind("Estimates"=coefs,"Std. Error"=stdErr))
else
return(coefs)
}
coef.ahg <- function(object,se=FALSE, amplitudeShift=FALSE, reparamPsi=FALSE,...){
return(coef(object$best,se=se, amplitudeShift=amplitudeShift,reparamPsi=reparamPsi))
}
###################################################
### chunk number 11:
###################################################
## convert between sin/cos and amplitude/shift formulation
###################################################
# y = gamma*sin(omega*t)+delta*cos(omega*t)
# = A*sin(omega*t + phi)
# with Amplitude A= sqrt(gamma^2+delta^2)
# and shift phi= arctan(delta/gamma)
#################################################
sinCos2amplitudeShift <- function(params){
# number of sin+cos terms
lengthParams <- length(params)
if(lengthParams %% 1 !=0)
stop("wrong number of params")
index.sin <- seq(1,lengthParams,by=2)
one <- function(i=1,params){
coef.sin <- params[i]
coef.cos <- params[i+1]
amplitude <- sqrt(coef.cos^2+coef.sin^2)
shift <- atan2(coef.cos, coef.sin)
return(c(amplitude,shift))
}
return(c(sapply(index.sin,one,params=params)))
}
amplitudeShift2sinCos <- function(params){
lengthParams <- length(params)
if (lengthParams%%1 != 0)
stop("wrong number of params")
index.A <- seq(1, lengthParams, by = 2)
one <- function(i = 1, params) {
coef.A <- params[i]
coef.shift <- params[i + 1]
coef.cos <- -coef.A*tan(coef.shift)/sqrt(1+tan(coef.shift)^2)
coef.sin <- -coef.A/sqrt(1+tan(coef.shift)^2)
return(c(coef.sin,coef.cos))
}
return(c(sapply(index.A, one, params = params)))
}
##############################################
# y = gamma*sin(omega*t)+delta*cos(omega*t)
# g(gamma,delta) = [sqrt(gamma^2+delta^2), arctan(delta/gamma) ]'
# compute jacobian (dg_i(x)/dx_j)_ij
#############################################
jacobianAmplitudeShift <- function(params){
# number of sin+cos terms
lengthParams <- length(params)
if(lengthParams %% 1 !=0)
stop("wrong number of params")
index.sin <- seq(1,lengthParams,by=2)
# function to compute jacobian of the transformation sinCos2AmplitudeShift()
one <- function(i=1,params){
coef.sin <- params[i]
coef.cos <- params[i+1]
dAmplitude.dcoef.sin <- coef.sin/sqrt(coef.cos^2+coef.sin^2)
dAmplitude.dcoef.cos <- coef.cos/sqrt(coef.cos^2+coef.sin^2)
dShift.dcoef.sin <- - coef.cos/(coef.cos^2+coef.sin^2)
dShift.dcoef.cos <- coef.sin/(coef.cos^2+coef.sin^2)
return(c(dAmplitude.dcoef.sin,dShift.dcoef.sin,dAmplitude.dcoef.cos,dShift.dcoef.cos))
}
jacobi<-sapply(index.sin,one,params=params)
res <- matrix(0,nrow=lengthParams,ncol=lengthParams)
j<-0
for (i in index.sin){
j<-j+1
res[i:(i+1),i:(i+1)] <- jacobi[,j]
}
return(res)
}
###################################################
### chunk number 12:
###################################################
## additional (undocumented) functions needed for algo.hhh
######################################################################
# Function to unpack params and ensure that autoregressive parameters
# lambda and phi are positive
# and proportion parameter is 0 < pi < 1
#
# theta - (alpha_i, lambda, phi, prop, beta, gamma_i, delta_i, psi)
# designRes - result of a call to make.design
######################################################################
unpackParams <- function(theta, designRes){
dimIntercept <- designRes$dimTheta$intercept
dimLambda <- designRes$dimTheta$lambda
indexLambda <- dimIntercept+dimLambda
dimPhi <- designRes$dimTheta$phi
indexPhi <- indexLambda +dimPhi
dimProportion <- designRes$dimTheta$proportion
indexProportion <- indexPhi+dimProportion
dimTrend <- designRes$dimTheta$trend
indexTrend <- indexProportion+dimTrend
dimSeason <- designRes$dimTheta$season
indexSeason <- indexTrend +dimSeason
dimNegbin <- designRes$dimTheta$negbin
# params set to NULL if not specified
# intercept always
alpha <- theta[1:dimIntercept]
if(dimLambda >0)
lambda <- exp(theta[(dimIntercept+1):indexLambda])
else lambda <- NULL
if(dimPhi >0)
phi <- exp(theta[(indexLambda+1):(indexPhi)])
else phi <- NULL
if(dimProportion >0){
prop <- theta[(indexPhi+1):indexProportion]
# ensure that proportion is 0<pi<1
prop <- exp(prop)/(1+exp(prop))
} else prop <- NULL
if(dimTrend >0)
beta <- theta[(indexProportion+1):indexTrend]
else beta <- NULL
if(dimSeason >0)
gamma <- theta[(indexTrend+1):indexSeason]
else gamma <- NULL
if(dimNegbin >0)
psi <- exp(theta[(indexSeason+1):(indexSeason+dimNegbin)])
else psi <- NULL
return(list(alpha=alpha,lambda=lambda, phi=phi,pi=prop,beta=beta, gamma=gamma, psi=psi))
}
#############################################
# function to compute gradient of loglikelihood
# -> used in optim
################################################
gradient <- function(theta,designRes){
if(any(is.na(theta) | !is.finite(theta)))
return(rep(NA,length(theta)))
Y<-designRes$Y
Ym1 <-designRes$Ym1
control <- designRes$control
mean <- meanResponse(theta=theta, designRes=designRes)
params <- unpackParams(theta,designRes)
nOfNeighbours <- designRes$nOfNeighbours
nhood <- designRes$disProgObj$neighbourhood
nareas <- ncol(Y)
endemic <- mean$endemic
meanTotal <- mean$mean
## helper function for derivatives:
# negbin model or poisson model
if(control$negbin!="none"){
psi <- matrix(params$psi,ncol=nareas,nrow=nrow(Y),byrow=TRUE)
psiPlusMu <- psi + meanTotal
# helper function for derivatives: negbin
derivHHH <- function(dmu){
# if(any(dim(dmu)!=dim(Y)))
# cat("warning: dimensions wrong \n")
(-psi/psiPlusMu +Y/meanTotal -Y/psiPlusMu)*dmu
}
} else {
# helper function for derivatives: poisson
derivHHH <- function(dmu){
# if(any(dim(dmu)!=dim(dmu)))
# cat("warning: dimensions wrong \n")
Y *(dmu/meanTotal) - dmu
}
}
###########################################
## epidemic part
##########################################
# model with lambda and phi
if(designRes$dimTheta$proportion == 0){
# gradient for lambda
if(designRes$dimTheta$lambda >0){
lambda <- params$lambda
if(length(control$lambda)>1){
# create vector lambda with elements 0 if control$lambda=FALSE
lambda <- rep(0,nareas)
lambda[!is.na(designRes$control$lambda)] <- params$lambda
}
lambda <- matrix(lambda,ncol=nareas,nrow=nrow(Y),byrow=TRUE)
dLambda <- derivHHH(lambda*designRes$Ym1)
# multiple lambda_i's or single lambda ?
if(length(control$lambda) > 1)
grLambda <- colSums(dLambda)[!is.na(designRes$control$lambda)]
else grLambda <- sum(dLambda)
if(any(is.na(grLambda))){
warning("derivatives for lambda not computable\n")
return(rep(NA,length(theta)))
}
} else grLambda <- NULL
# gradient for phi
if(designRes$dimTheta$phi >0){
phi <- params$phi
if(length(control$neighbours)>1){
# create vector phi with elements 0 if control$neighbours=FALSE
phi <- rep(0,nareas)
phi[!is.na(designRes$control$neighbours)] <- params$phi
}
phi <- matrix(phi,ncol=nareas,nrow=nrow(Y),byrow=TRUE)
if(any(is.na(phi))) stop("phi contains NA\'s\n")
dPhi <- derivHHH(phi*designRes$Ym1.neighbours)
# multiple phi_i's or single phi ?
if(length(control$neighbours)>1)
grPhi <- colSums(dPhi)[!is.na(designRes$control$neighbours)]
else grPhi<- sum(dPhi)
if(any(is.na(grPhi))){
warning("derivatives for phi not computable\n")
return(rep(NA,length(theta)))
}
} else grPhi <- NULL
# gradient for proportion pi
grPi <- NULL
} else {
################################################
## model with lambda and proportion pi
###############################################
## gradient for lambda
gradLambda <- function(prop,lambda){
# ensure region id is not included
diag(nhood) <- 0
# compute lambda_id* [pi_id*Ym1_id + sum_j~id {(1-pi_id )/|j~id|* Ym1_id}] for unit id
dLambda.id <- function(id){
# number of Neigbours for unit id, i.e. |k~id|
n<-nOfNeighbours[id]
lambdaYm1.id <- Ym1[,id]*lambda[id]
pi.id.j <- rep(0,nareas)
pi.id.j[id]<- prop[id]
pi.id.j[nhood[,id]>0] <-(1-prop[id])/n
lambdaYm1pi.id <-lambdaYm1.id*matrix(pi.id.j,ncol=nareas,nrow=nrow(Ym1),byrow=TRUE)
# d/dpi log L(mu_i,t)
return(rowSums(derivHHH(lambdaYm1pi.id)))
}
return(sapply(1:nareas,dLambda.id))
}
## gradient for pi
gradPi <- function(prop,lambda){
# ensure region id is not included
diag(nhood) <- 0
# compute (pi_id-pi_id^2)* [lambda_id*Ym1_id - sum_j~id {lambda_id/|j~id|* Ym1_id}] for unit id
dPi.id <- function(id){
# number of Neigbours for unit id, i.e. |k~id|
n<-nOfNeighbours[id]
dPiYm1.id <- Ym1[,id]*(prop[id]-prop[id]^2)
lambda.id.j <- rep(0,nareas)
lambda.id.j[id]<- lambda[id]
lambda.id.j[nhood[,id]>0] <-(-lambda[id])/n
dPiYm1lambda.id <-dPiYm1.id*matrix(lambda.id.j,ncol=nareas,nrow=nrow(Ym1),byrow=TRUE)
# d/dpi log L(mu_i,t)
return(rowSums(derivHHH(dPiYm1lambda.id)))
}
return(sapply(1:nareas,dPi.id))
}
# gradient for lambda
if(designRes$dimTheta$lambda ==0)
cat("no lambda\n")
lambda <- rep(params$lambda,length=nareas)
prop <- rep(params$pi, length=nareas)
dLambda <- gradLambda(prop=prop,lambda=lambda)
# multiple lambda_i's or single lambda ?
if(designRes$dimTheta$lambda > 1)
grLambda <- colSums(dLambda)
else grLambda <- sum(dLambda)
if(any(is.na(grLambda))){
warning("derivatives for lambda not computable\n")
return(rep(NA,length(theta)))
}
# gradient for phi
grPhi <- NULL
# gradient for proportion pi
dPi <- gradPi(prop=prop,lambda=lambda)
if(designRes$dimTheta$proportion >1)
grPi <- colSums(dPi)
else grPi <- sum(dPi)
if(any(is.na(grPi))){
warning("derivatives for pi not computable\n")
return(rep(NA,length(theta)))
}
}
############################################
## endemic part
############################################
# gradient for intercepts
grAlpha <- colSums(derivHHH(endemic))
if(any(is.na(grAlpha))){
warning("derivatives for alpha not computable\n")
return(rep(NA,length(theta)))
}
# gradient for trend
if(designRes$dimTheta$trend >0){
dTrend <- derivHHH(endemic*designRes$X.trendSeason[,1])
if(designRes$dimTheta$trend >1)
grTrend <- colSums(dTrend)[designRes$control$linear]
else grTrend <- sum(dTrend)
if(any(is.na(grTrend))){
warning("derivatives for trend not computable\n")
return(rep(NA,length(theta)))
}
} else grTrend <- NULL
# gradient for season
grSeason <- NULL
if(designRes$dimTheta$season >0){
## single or multiple seasonal params
if(length(control$nseason)==1){
for (i in ((designRes$dimTheta$trend>0) +1):ncol(designRes$X.trendSeason) ){
grSeason <- c(grSeason, sum(derivHHH(endemic*designRes$X.trendSeason[,i])))
}
if(any(is.na(grSeason))){
warning("derivatives for seasonal parameters not computable\n")
return(rep(NA,length(theta)))
}
} else if(length(control$nseason)==nareas){
#maximum number of Fourier frequencies S.max=max_i{S_i}
maxSeason <- 2*max(control$nseason)
grSeason <- matrix(NA,nrow=maxSeason,ncol=ncol(Y))
for (j in ((designRes$dimTheta$trend>0) +1):(maxSeason+(designRes$dimTheta$trend>0) ) ){
# compute derivatives of gamma_{ij}, j= 1, ..., 2*S.max
grSeason[j-(designRes$dimTheta$trend>0),] <- colSums(derivHHH(endemic*designRes$X.trendSeason[,j]))
# set gradients for gamma_{ij} to NA if j > S_i
grSeason[j-(designRes$dimTheta$trend>0),(j > (2*control$nseason)+(designRes$dimTheta$trend>0))] <- NA
}
# gradient now is in order sin(omega_1)_A, sin(omega_1)_B, sin(omega_1)_C, ...
# cos(omega_1)_A, cos(omega_1)_B, cos(omega_1)_C, ...
# sin(omega_2)_A, sin(omega_2)_B, sin(omega_2)_C, ...
# ...
# and needs to be in the following order:
# sin(omega_1)_A, cos(omega_1)_A, sin(omega_2)_A, ..., cos(omega_S.max)_A
# sin(omega_1)_B, cos(omega_1)_B, sin(omega_2)_B, ..., cos(omega_S.max)_B
# remove NA's, i.e. only derivatives for {gamma_{ij}: j <=2*S_i}
# check if there are any NaN's
if(any(is.nan(grSeason))){
warning("derivatives for seasonal parameters not computable\n")
return(rep(NA,length(theta)))
}
grSeason <- grSeason[!is.na(grSeason)]
} # end multiple params
} # end gradient season
# gradient for psi
if(designRes$dimTheta$negbin>0){
dPsi <- psi*(digamma(Y+psi)-digamma(psi) +log(psi)+1 - log(psiPlusMu) -psi/psiPlusMu -Y/psiPlusMu)
# multiple psi_i's or single psi?
if(designRes$dimTheta$negbin >1)
grPsi <- colSums(dPsi)
else grPsi <- sum(dPsi)
if(any(is.na(grPsi))){
warning("derivatives for psi not computable\n")
return(rep(NA,length(theta)))
}
} else grPsi <- NULL
res <- c(grAlpha,grLambda,grPhi,grPi,grTrend,grSeason,grPsi)
return(res)
}
################################
# Calculates the weighted sum of counts of adjacent areas
# weights are specified in neighbourhood-matrix of the disProgObj
# (experimental atm)
#
# \nu_i,t = \lambda_y_i,t-1 + \phi*\sum_(j~i) [w_ji*y_j,t-1]
#
# disProgObj$neighbourhood can either be a matrix with weights w_ji (in columns)
# or an array (for time varying weights)
#
# if the neighbourhood-matrix has elements 1 if i~j and 0 otherwise
# weightedSumNeighbours() = sumNeighbours()
###########################################
weightedSumNeighbours <- function(disProgObj){
observed <- disProgObj$observed
ntime<-nrow(observed)
narea<-ncol(observed)
neighbours <- matrix(nrow=ntime,ncol=narea)
nhood <- disProgObj$neighbourhood
#check neighbourhood
if(any(is.na(nhood)))
stop("No correct neighbourhood matrix given\n")
## constant neighbourhood (over time)?
if(length(dim(nhood))==2){
# ensure only neighouring areas are summed up
diag(nhood) <- 0
nhood <- array(nhood,c(narea,narea,ntime))
} else if(length(dim(nhood))==3){
if(any(dim(nhood)[1:2]!= narea) | dim(nhood)[3] != ntime)
stop("neighbourhood info incorrect\n")
}
# number of neighbours
nOfNeighbours <-colSums(nhood[,,1]>0)
for(i in 1:ncol(observed)){
#weights <- matrix(as.numeric(nhood[,i]),nrow=nrow,ncol=ncol,byrow=TRUE)
weights <- t(nhood[,i,])
neighbours[,i] <- rowSums(observed*weights)
}
return(list(neighbours=neighbours, nOfNeighbours=nOfNeighbours))
}
#################################################
# params psi, lambda and phi are on log-scale
# -> transformation of estimates, standard errors and fisher (using delta rule)
# labels for results
#
# g(theta) = (exp(lambda), exp(phi), beta, gamma, delta, exp(psi), alpha)
# D is the Jacobian of g
# D = diag(exp(lambda), exp(phi), 1, 1, 1, exp(psi), 1)
#########################################
jacobian <- function(thetahat, designRes){
dimtheta <- designRes$dimTheta$dim
nareas <- ncol(designRes$disProgObj$observed)
thetaNames <- NULL
D <-diag(1,ncol=dimtheta,nrow=dimtheta)
dimLambda <- designRes$dimTheta$lambda
dimPhi <- designRes$dimTheta$phi
dimPi <- designRes$dimTheta$proportion
dimTrend <- designRes$dimTheta$trend
dimPsi <- designRes$dimTheta$negbin
dimSeason <-designRes$dimTheta$season
nseason <- designRes$control$nseason
alpha <- colnames(designRes$disProgObj$observed)
if(is.null(alpha)) alpha <- paste("obs",1:nareas, sep="")
thetaNames <- c(thetaNames, alpha)
if(dimLambda >0){
if(length(designRes$control$lambda)==1)
lambda <- "lambda"
else {
lambda <- paste("lambda", alpha, sep="_")[!is.na(designRes$control$lambda)]
}
thetaNames <- c(thetaNames, lambda)
index <-(nareas+1):(nareas+dimLambda)
thetahat[index] <- exp(thetahat[index])
diag(D)[index] <- thetahat[index]
}
if(dimPhi >0){
if(length(designRes$control$neighbours)==1)
phi <- "phi"
else {
phi <- paste("phi", alpha, sep="_")[!is.na(designRes$control$neighbours)]
}
thetaNames <- c(thetaNames, phi)
index <- (nareas+dimLambda+1):(nareas+dimLambda+dimPhi)
thetahat[index] <- exp(thetahat[index])
diag(D)[index] <- thetahat[index]
}
if(dimPi>0){
prop <- switch(designRes$control$proportion,
"single"="pi",
"multiple"=paste("pi", alpha, sep="_"))
thetaNames <- c(thetaNames, prop)
index <- (nareas+dimLambda+dimPhi+1):(nareas+dimLambda+dimPhi+dimPi)
exp.pi <- exp(thetahat[index])
diag(D)[index] <- exp.pi/((1+exp.pi)^2)
thetahat[index] <- exp.pi/(1+exp.pi)
}
if(dimTrend >0){
beta <- colnames(designRes$X.trendSeason)[1]
if(length(designRes$control$linear)>1)
beta <- paste(beta,alpha,sep="_")[designRes$control$linear]
thetaNames <- c(thetaNames, beta)
}
if(dimSeason > 0){
maxSeason <- 2*max(nseason)
sinCos <- rep(colnames(designRes$X.trendSeason)[(1+ (dimTrend>0) ):((dimTrend>0) +maxSeason)], length=maxSeason)
if(length(nseason)==1){
gammaDelta <- sinCos
} else if(length(nseason==nareas)){
gammaDelta <- matrix(NA,ncol=nareas,nrow=maxSeason)
for(i in 1:nareas){
gammaDelta[0:(2*nseason[i]),i] <- paste(sinCos,alpha[i],sep="_")[0:(2*nseason[i])]
}
gammaDelta <- gammaDelta[!is.na(gammaDelta)]
}
thetaNames <- c(thetaNames, gammaDelta )
}
if(dimPsi >0){
psi <- switch(designRes$control$negbin,
"single"="psi",
"multiple"=paste("psi",alpha,sep="_"))
thetaNames <- c(thetaNames, psi)
index <- (dimtheta-dimPsi+1):dimtheta
thetahat[index] <- exp(thetahat[index])
diag(D)[index] <- thetahat[index]
}
dimnames(D) <- list(thetaNames,thetaNames)
names(thetahat) <- thetaNames
return(list(D=D,theta=thetahat))
}
# theta.epidemic = c(lambda,phi)
# Note: lambda and phi are on log-scale
getLambda <- function(theta.epidemic, designRes, t.weights=1){
# check dimension of theta.epidemic
dimLambda <- designRes$dimTheta$lambda
dimPhi <- designRes$dimTheta$phi
if(designRes$dimTheta$proportion>0)
stop("proportions currently not supported\n")
if(length(theta.epidemic)!= (dimLambda+dimPhi))
stop("vector with parameters must be of length ", dimLambda+dimPhi,"\n")
# is there an autoregression?
if(sum(!is.na(designRes$control$lambda))==0 & sum(!is.na(designRes$control$neighbours)) ==0)
return(NULL)
if(dimLambda>0){
coef.lambda <- exp(theta.epidemic[1:dimLambda] )
} else coef.lambda <- 0
if(dimPhi>0){
coef.phi <- exp(theta.epidemic[(dimLambda+1):length(theta.epidemic)] )
} else coef.phi <- 0
#univariate?
if(ncol(designRes$disProgObj$observed)==1){
if(sum(!is.na(designRes$control$lambda))==1)
return(coef.lambda)
else return(NULL)
}
nareas <- ncol(designRes$Y) #ncol(nhood)
if(designRes$control$proportion=="none"){
# no lambda
if(sum(!is.na(designRes$control$lambda))==0){
lambda <- rep(0,nareas)
# single lambda for all units
} else if(sum(!is.na(designRes$control$lambda))==1 & length(designRes$control$lambda)==1){
lambda <- rep(coef.lambda,nareas)
# multiple lambda
} else{
lambda <- rep(0, nareas)
lambda[designRes$control$lambda] <- coef.lambda
}
Lambda <- diag(lambda,nareas)
if(dimPhi>0){
# extract neighbourhood, i.e. weight matrix
nhood <- designRes$disProgObj$neighbourhood
# time-varying weights w_ji
if(length(dim(nhood))==3)
nhood <- nhood[,,t.weights]
# ensure the diagonal is zero
diag(nhood) <- 0
nOfNeighbours <- colSums(nhood>0)
# single phi for all units
if(length(designRes$control$neighbours)==1 & sum(!is.na(designRes$control$neighbours))==1){
phi <-rep(coef.phi,nareas)
} else if(length(designRes$control$neighbours)>1 & sum(!is.na(designRes$control$neighbours))>0){
phi <- rep(0,nareas)
phi[!is.na(designRes$control$neighbours)] <- coef.phi
}
phi.weights <- matrix(phi,nrow=nareas,ncol=nareas,byrow=FALSE)*nhood
Lambda[nhood>0] <- phi.weights[nhood>0]
}
} else { #todo: check
return(NULL)
#hoehle 14 Oct 2008 - commented, coz it contains warnings for R CMD check
# lambdaMatrix <- matrix(lambda,ncol=nareas,nrow=nareas,byrow=TRUE)
# nOfNeighbours <- rowSums(nhood)
# piMatrix <- matrix((1-prop)/nOfNeighbours,ncol=nareas,nrow=nareas,byrow=TRUE)
# piMatrix[nhood==0] <-0
# diag(piMatrix)<-prop
# Lambda <- lambdaMatrix*piMatrix
}
return(Lambda)
}
## moment estimator of exp(alpha)
## alpha.hat(lambda,phi) = mean(y)' %*% (I - Lambda)
expAlpha.mm <- function(Lambda,Y){
mean.obs <- colMeans(Y)
mean.obs %*% (diag(1,length(mean.obs))-Lambda)
}
########
logLik.ah <- function(object,...){
if(!inherits(object, "ah"))
stop("expected object to be an object of class ah\n")
if(!object$convergence)
stop("algorithm did not converge\n")
val <- object$loglikelihood
attr(val, "df") <- length(coef(object))
attr(val, "nobs") <- object$nObs
class(val) <- "logLik"
return(val)
}
logLik.ahg <- function(object, ...){
logLik.ah(object$best)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.