R/lifetable.R

Defines functions e0 flife.expectancy life.expectancy get.e0 print.lifetable as.data.frame.lifetable lines.lifetable plot.lifetable lifetable

Documented in e0 flife.expectancy life.expectancy lifetable lines.lifetable plot.lifetable

# Lifetable functions
# Produce lifetable from mortality rates

#' Construct lifetables from mortality rates
#'
#' Computes period and cohort lifetables from mortality rates for multiple years.
#'
#' For period lifetables, all years and all ages specified are included in the tables. For cohort lifetables,
#' if \code{ages} takes a scalar value, then the cohorts are taken to be of that age in each year contained in \code{years}.
#' But if \code{ages} is a vector of values, then the cohorts are taken to be of those ages in the first year contained in \code{years}.
#'
#' For example, if \code{ages=0} then lifetables of the birth cohorts for all years in \code{years} are computed. On the other hand,
#' if \code{ages=0:100} and \code{years=1950:2010}, then lifetables of each age cohort in 1950 are computed.
#'
#' In all cases, \eqn{q_x = m_x/(1+[(1-a_x)m_x])}{qx = mx/(1 + ((1-ax) * mx))} as per Chiang (1984).
#'
#' Warning: the code has only been tested for data based on single-year age groups.
#'
#' @param data Demogdata object such as obtained from \code{\link{read.demogdata}},
#' \code{\link{forecast.fdm}} or \code{\link{forecast.lca}}.
#' @param series Name of series to use.  Default is the first series in \code{data[["rate"]]}.
#' @param years Vector indicating which years to include in the tables.
#' @param ages Vector indicating which ages to include in table.
#' @param max.age Age for last row. Ages beyond this are combined.
#' @param type Type of lifetable: \code{period} or \code{cohort}.
#'
#' @return Object of class \dQuote{lifetable} containing the following components:
#' \item{label}{Name of region from which data are taken.}
#' \item{series}{Name of series}
#' \item{age}{Ages for lifetable}
#' \item{year}{Period years or cohort years}
#' \item{mx}{Death rate at age x.}
#' \item{qx}{The probability that an individual of exact age x will die before exact age x+1.}
#' \item{lx}{Number of survivors to exact age x.  The radix is 1.}
#' \item{dx}{The number of deaths between exact ages x and x+1.}
#' \item{Lx}{Number of years lived between exact age x and exact age x+1.}
#' \item{Tx}{Number of years lived after exact age x.}
#' \item{ex}{Remaining life expectancy at exact age x.}
#' Note that the lifetables themselves are not returned, only their components. However, there is a print method that constructs (and returns)
#' the lifetables from the above components.
#'
#' @seealso \code{\link{life.expectancy}}
#' @author Heather Booth, Leonie Tickle, Rob J Hyndman, John Maindonald and Timothy Miller
#' @references
#' Chiang CL. (1984) \emph{The life table and its applications}. Robert E Krieger Publishing Company: Malabar.
#'
#' Keyfitz, N, and Caswell, H. (2005) \emph{Applied mathematical demography}, Springer-Verlag: New York.
#'
#' Preston, S.H., Heuveline, P., and Guillot, M. (2001) \emph{Demography: measuring and modeling population processes}. Blackwell
#'
#' @examples
#' france.lt <- lifetable(fr.mort)
#' plot(france.lt)
#' lt1990 <- print(lifetable(fr.mort,year=1990))
#'
#' france.LC <- lca(fr.mort)
#' france.fcast <- forecast(france.LC)
#' france.lt.f <- lifetable(france.fcast)
#' plot(france.lt.f)
#'
#' # Birth cohort lifetables, 1900-1910
#' france.clt <- lifetable(fr.mort,type="cohort",age=0, years=1900:1910)
#'
#' # Partial cohort lifetables for 1950
#' lifetable(fr.mort, years=1950)
#' @keywords models
#' @export
lifetable <- function(data, series=names(data$rate)[1], years=data$year, ages=data$age,
  max.age=min(100,max(data$age)), type=c("period","cohort"))
{
  if(!is.element("demogdata",class(data)))
    stop("data must be a demogdata object")
  if(data$type != "mortality")
    stop("data must be a mortality object")
  type <- match.arg(type)
  if(!is.el(series,names(data$rate)))
    stop(paste("Series",series,"not found"))
  if(is.na(sum(match(years,data$year))))
    stop("Years not present in data")
  sex <- series
  if(sex!="female" & sex!="male" & sex!="total")
  {
    if(is.element("model",names(data)))
      sex <- names(data$model)[4]
  }
  na <- length(data$age)
  if(na > 4)
    agegroup <- data$age[na-1]-data$age[na-2]
  else
    stop("Insufficient age groups")

  if(type=="period")
  {
    max.age <- min(max.age,max(ages))
    data <- extract.ages(data,ages,combine.upper=FALSE)
    if(max.age < max(ages))
      data <- extract.ages(data,min(ages):max.age,combine.upper=TRUE)
    data <- extract.years(data,years=years)
    mx <- get.series(data$rate,series)
    n <- length(years)
    p <- nrow(mx)
    rownames(mx) <- ages <- data$age
    colnames(mx) <- years
    qx <- lx <- dx <- Lx <- Tx <- ex <- rx <- mx*NA
    rownames(rx) <- ages - 1
    rownames(rx)[ages==0] <- "B"
    for(i in 1:n)
    {
      ltable <- lt(mx[,i],min(ages),agegroup,sex)
      nn <- length(ltable$qx)
      qx[1:nn,i] <- ltable$qx
      lx[1:nn,i] <- ltable$lx
      dx[1:nn,i] <- ltable$dx
      Lx[1:nn,i] <- ltable$Lx
      Tx[1:nn,i] <- ltable$Tx
      ex[1:nn,i] <- ltable$ex
      rx[1:nn,i] <- ltable$rx
    }
  }
  else if(type=="cohort" & length(ages)>1) # multiple ages, single year.
  {
    data <- extract.ages(data,min(ages):max.age,combine.upper=TRUE)
    data <- extract.years(data,years=seq(min(years),max(data$year),by=1))
    n <- length(data$year)
    p <- length(data$age)
    cmx <- matrix(NA,p,p)
    rownames(cmx) <- data$age
    colnames(cmx) <- paste(min(years)," age ",data$age,sep="")
    qx <- dx <- Tx <- lx <- Lx <- ex <- cmx
    cohort <- match(ages,data$age)
    cohort <- cohort[!is.na(cohort)]
    if(length(cohort)==0)
      stop("No data available")
    if(min(data$age[cohort]+n-1) < max.age)
      warning("Insufficient data for other lifetables. Try reducing max.age")
    for(coh in cohort)
    {
      if(data$age[coh]+n-1 > max.age)
      {
        subdata <- extract.ages(data,data$age[coh]:max.age,combine.upper=TRUE)
        mx <- get.series(subdata$rate,series)
        p <- nrow(mx)
        for (j in 1:p)
          cmx[coh+j-1,coh] <- mx[j,j]
        ltable <- lt(cmx[coh+(1:p)-1,coh], data$age[coh], agegroup, sex=sex)
        p <- length(ltable$lx)
        lx[coh+(1:p)-1,coh] <- ltable$lx
        Lx[coh+(1:p)-1,coh] <- ltable$Lx
        ex[coh+(1:p)-1,coh] <- ltable$ex
        qx[coh+(1:p)-1,coh] <- ltable$qx
        dx[coh+(1:p)-1,coh] <- ltable$dx
        Tx[coh+(1:p)-1,coh] <- ltable$Tx
      }
    }
    mx <- cmx
    # Retain columns in required cohort
    mx <- mx[,cohort,drop=FALSE]
    lx <- lx[,cohort,drop=FALSE]
    Lx <- Lx[,cohort,drop=FALSE]
    ex <- ex[,cohort,drop=FALSE]
    qx <- qx[,cohort,drop=FALSE]
    dx <- dx[,cohort,drop=FALSE]
    Tx <- Tx[,cohort,drop=FALSE]
    rx <- NULL
  }
  else #single age, multiple years.
  {
    data <- extract.years(data,years=seq(min(years),max(data$year),by=1))
    data <- extract.ages(data,ages:max.age,combine.upper=TRUE)
    n <- length(data$year)
    p <- length(data$age)
    ny <- length(years)
    cmx <- matrix(NA,p,ny)
    rownames(cmx) <- data$age
    colnames(cmx) <- paste(years," age ",ages,sep="")
    qx <- dx <- Tx <- lx <- Lx <- ex <- cmx
    minage <- max.age
    for(i in 1:ny)
    {
      subdata <- extract.years(data,years=seq(years[i],max(data$year),by=1))
      upperage <- min(ages+length(subdata$year)-1)
      minage <- min(minage,upperage)
      if(upperage >= max.age)
      {
        mx <- get.series(subdata$rate,series)
        p <- nrow(mx)
        for (j in 1:p)
          cmx[j,i] <- mx[j,j]
        ltable <- lt(cmx[,i],ages, agegroup, sex=sex)
        p <- length(ltable$lx)
        lx[1:p,i] <- ltable$lx
        Lx[1:p,i] <- ltable$Lx
        ex[1:p,i] <- ltable$ex
        qx[1:p,i] <- ltable$qx
        dx[1:p,i] <- ltable$dx
        Tx[1:p,i] <- ltable$Tx
      }
    }
    mx <- cmx
    rx <- NULL
  }

  return(structure(list(age=ages,year=years, mx=mx,qx=qx,lx=lx,dx=dx,Lx=Lx,Tx=Tx,ex=ex,rx=rx,
        series=series, type=type, label=data$label),class="lifetable"))
}



lt <- function (mx, startage = 0, agegroup = 5, sex)
{
  # Omit missing ages
  if (is.na(mx[1]))
    mx[1] <- 0
  firstmiss <- (1:length(mx))[is.na(mx)][1]
  if (!is.na(firstmiss))
    mx <- mx[1:(firstmiss - 1)]
  nn <- length(mx)
  if (nn < 1)
    stop("Not enough data to proceed")

  # Compute width of each age group
  if (agegroup == 1)
    nx <- c(rep(1, nn - 1), Inf)
  else if (agegroup == 5) # First age group 0, then 1-4, then 5-year groups.
    nx <- c(1, 4, rep(5, nn - 3), Inf)
  else stop("agegroup must be either 1 or 5")

  if (agegroup == 5 & startage > 0 & startage < 5)
    stop("0 < startage < 5 not supported for 5-year age groups")

  if (startage == 0) # for single year data and the first age (0) in 5-year data
  {
    if (sex == "female")
    {
      if (mx[1] < 0.107)
        a0 <- 0.053 + 2.8 * mx[1]
      else a0 <- 0.35
    }
    else if (sex == "male")
    {
      if (mx[1] < 0.107)
        a0 <- 0.045 + 2.684 * mx[1]
      else a0 <- 0.33
    }
    else # if(sex == "total")
    {
      if (mx[1] < 0.107)
        a0 <- 0.049 + 2.742 * mx[1]
      else a0 <- 0.34
    }
  }
  else if (startage > 0)
    a0 <- 0.5
  else
    stop("startage must be non-negative")
  if (agegroup == 1)
  {
    if (nn > 1)
      ax <- c(a0, rep(0.5, nn - 2), Inf)
    else
      ax <- Inf
  }
  else if (agegroup == 5 & startage == 0)
  {
    if (sex == "female")
    {
      if (mx[1] < 0.107)
        a1 <- 1.522 - 1.518 * mx[1]
      else
        a1 <- 1.361
    }
    else if (sex == "male")
    {
      if (mx[1] < 0.107)
        a1 <- 1.651 - 2.816 * mx[1]
      else
        a1 <- 1.352
    }
    else # sex == "total"
    {
      if (mx[1] < 0.107)
        a1 <- 1.5865 - 2.167 * mx[1]
      else a1 <- 1.3565
    }
    ax <- c(a0, a1, rep(2.6, nn - 3), Inf)
    ### ax=2.5 known to be too low esp at low levels of mortality
  }
  else # agegroup==5 and startage > 0
  {
    ax <- c(rep(2.6, nn - 1), Inf)
    nx <- c(rep(5, nn))
  }
  qx <- nx * mx/(1 + (nx - ax) * mx)
  # age <- startage + cumsum(nx) - 1
  # if (max(age) >= 75) {
  #    idx <- (age >= 75)
  #   ax[idx] <- (1/mx + nx - nx/(1 - exp(-nx * mx)))[idx]
  #  qx[idx] <- 1 - exp(-nx * mx)[idx]
  #    }
  #qx[qx > 1] <- 1  ################  NOT NEEDED IN THEORY

  #plot(qx)  #### TO CHECK RESULT RE QX>1

  qx[nn] <- 1
  if (nn > 1)
  {
    lx <- c(1, cumprod(1 - qx[1:(nn - 1)]))
    dx <- -diff(c(lx, 0))
  }
  else
    lx <- dx <- 1
  Lx <- nx * lx - dx * (nx - ax)
  Lx[nn] <- lx[nn]/mx[nn]
  Tx <- rev(cumsum(rev(Lx)))
  ex <- Tx/lx
  if (nn > 2)
    rx <- c(Lx[1]/lx[1], Lx[2:(nn - 1)]/Lx[1:(nn - 2)], Tx[nn]/Tx[nn-1])
  else if (nn == 2)
    rx <- c(Lx[1]/lx[1], Tx[nn]/Tx[nn - 1])
  else
    rx <- c(Lx[1]/lx[1])
  if (agegroup == 5)
    rx <- c(0, (Lx[1] + Lx[2])/5 * lx[1], Lx[3]/(Lx[1]+Lx[2]),
            Lx[4:(nn - 1)]/Lx[3:(nn - 2)], Tx[nn]/Tx[nn-1])
  result <- data.frame(ax = ax, mx = mx, qx = qx, lx = lx,
      dx = dx, Lx = Lx, Tx = Tx, ex = ex, rx = rx, nx = nx)
  return(result)
}


#' Plot life expectancy from lifetable
#'
#' plots life expectancy for each age and each year as functional time series.
#'
#' @param x Output from \code{\link{lifetable}}.
#' @param years Years to plot. Default: all available years.
#' @param main Main title.
#' @param xlab Label for x-axis.
#' @param ylab Label for y-axis.
#' @param ... Additional arguments passed to \code{\link[rainbow]{plot.fds}}.
#'
#' @seealso \code{\link{life.expectancy}}, \code{\link{lifetable}}.
#' @author Rob J Hyndman
#' @examples
#' france.lt <- lifetable(fr.mort)
#' plot(france.lt)
#'
#' france.LC <- lca(fr.mort)
#' france.fcast <- forecast(france.LC)
#' france.lt.f <- lifetable(france.fcast)
#' plot(france.lt.f,years=2010)
#' @export
#' @keywords models
plot.lifetable <- function(x,years=x$year,main,xlab="Age",ylab="Expected number of years left",...)
{
  if(x$type != "period")
    stop("Currently only period lifetables can be plotted.")
  # Extract years
  idx <- match(years,x$year)
  idx <- idx[!is.na(idx)]
  idx <- idx[idx <= ncol(x$ex)]
  if(length(idx)==0)
    stop("Year not available")
  years <- x$year[idx]
  ny <- length(years)

  if(missing(main))
  {
    main <- paste("Life expectancy:",x$label,x$series)
    if(ny>1)
      main <- paste(main," (",min(years),"-",max(years),")",sep="")
    else
      main <- paste(main," (",years,")",sep="")
  }

  plot(fts(x$age,x$ex[,idx],start=years[1],frequency=1),main=main,ylab=ylab,xlab=xlab,...)
}

#' @rdname plot.lifetable
#' @export
lines.lifetable <- function(x,years=x$year,...)
{
  if(x$type != "period")
    stop("Currently only period lifetables can be plotted.")
  # Extract years
  idx <- match(years,x$year)
  idx <- idx[!is.na(idx)]
  idx <- idx[idx <= ncol(x$ex)]
  if(length(idx)==0)
    stop("Year not available")
  years <- x$year[idx]
  ny <- length(years)

  lines(fts(x$age,x$ex[,idx],start=x$year[1],frequency=1),...)
}

#' @export
as.data.frame.lifetable <- function(x, ...) {
  ny <- ncol(x$ex)
  if(x$type=="period") {
    year <- x$year
  } else {
    year <- as.numeric(colnames(x$ex))
  }
  outlist <- vector(length=ny,mode="list")
  for(i in seq(ny)) {
    idx2 <- !is.na(x$mx[,i])
    if(sum(idx2)>0) {
      outlist[[i]] <- data.frame(year[i], as.numeric(rownames(x$ex)), x$mx[,i],x$qx[,i],x$lx[,i],x$dx[,i],x$Lx[,i],x$Tx[,i],x$ex[,i])[idx2,]
      colnames(outlist[[i]]) <- c("year","x","mx","qx","lx","dx","Lx","Tx","ex")
    }
  }
  out <- do.call("rbind", outlist)
  rownames(out) <- NULL
  return(out)
}

#' @export
print.lifetable <- function(x,digits=4,...)
{
  out <- as.data.frame(x)
  if(x$type=="period") {
    cat("Period ")
  } else {
    cat("Cohort ")
  }
  cat(paste("lifetable for",x$label,":",x$series,"\n\n"))
  print(round(out, digits = digits))
  invisible(out)
}

# Compute expected age from single year mortality rates
get.e0 <- function(x,agegroup,sex,startage=0)
{
  lt(x, startage, agegroup, sex)$ex[1]
}

# Compute expected ages for multiple years
#' Estimate life expectancy from mortality rates
#'
#' All three functions estimate life expectancy from \code{lifetable}.
#' The function \code{flife.expectancy} is primarily designed for forecast life expectancies and will optionally
#' produce prediction intervals. Where appropriate, it will package the results as a forecast object
#' which makes it much easier to product nice plots of forecast life expectancies.
#' The \code{e0} function is a shorthand wrapper for \code{flife.expectancy} with \code{age=0}.
#'
#' @return Time series of life expectancies (one per year), or a forecast object of life expectancies (one per year).
#'
#' @param data Demogdata object of type \dQuote{mortality} such as obtained from \code{\link{read.demogdata}},
#' or an object of class \code{fmforecast} such as the output from  \code{\link{forecast.fdm}} or \code{\link{forecast.lca}},
#' or an object of class \code{fmforecast2} such as the output from \code{\link{forecast.fdmpr}}.
#' @param series Name of mortality series to use. Default is the first demogdata series in data.
#' @param years Vector indicating which years to use.
#' @param type Either \code{period} or \code{cohort}.
#' @param age Age at which life expectancy is to be calculated.
#' @param max.age Maximum age for life table calculation.
#' @param PI If TRUE, produce a prediction interval.
#' @param nsim Number of simulations to use when computing a prediction interval.
#' @param ... Other arguments passed to \code{simulate} when producing prediction intervals.
#'
#' @seealso \code{\link{lifetable}}
#' @author Rob J Hyndman
#' @examples
#' plot(life.expectancy(fr.mort),ylab="Life expectancy")
#'
#' france.LC <- lca(fr.mort,adjust="e0",years=1950:1997)
#' france.fcast <- forecast(france.LC,jumpchoice="actual")
#' france.e0.f <- life.expectancy(france.fcast)
#'
#' france.fdm <- fdm(extract.years(fr.mort,years=1950:2006))
#' france.fcast <- forecast(france.fdm)
#' \dontrun{
#'   e0.fcast <- e0(france.fcast,PI=TRUE,nsim=200)
#'   plot(e0.fcast)}
#'
#' life.expectancy(fr.mort,type='cohort',age=50)
#'
#' @keywords models
#' @export
life.expectancy <- function(data,series=names(data$rate)[1],years=data$year,
    type=c("period","cohort"), age=min(data$age), max.age=min(100,max(data$age)))
{
  type <- match.arg(type)
  if(!is.el(series,names(data$rate)))
    stop(paste("Series",series,"not found"))
  if(is.null(max.age))
    max.age <- min(100,max(data$age))
  if(age > max.age | age > max(data$age))
    stop("age is greater than maximum age")
  else if(age < min(data$age))
    stop("age is less than minimum age")
  if(type=="period")
    data.lt <- lifetable(data,series,years,type=type,max.age=max.age)$ex
  else
   data.lt <- lifetable(data,series,years,type=type,ages=age,max.age=max.age)$ex
  idx <- match(age,rownames(data.lt))
  #if(sum(is.na(data.lt[idx,]))>0
  # max(data.lt[idx,]) > 1e9)
  #    warning("Some missing or infinite values in the life table calculation.\n  These can probably be avoided by setting max.age to a lower value.")

  return(ts(data.lt[idx,],start=years[1],frequency=1))
}

#' @rdname life.expectancy
#' @export
flife.expectancy <- function(data, series=NULL, years=data$year,
    type=c("period","cohort"), age, max.age=NULL, PI=FALSE, nsim=500, ...)
{
  type <- match.arg(type)
  if(is.element("fmforecast",class(data)))
  {
    if(data$type != "mortality")
       stop("data not a mortality object")
    hdata <- list(year=data$model$year, age=data$model$age,
         type=data$type, label=data$model$label, lambda=data$lambda)
    hdata$rate <- list(data$model[[4]])
    if(min(hdata$rate[[1]],na.rm=TRUE) < 0 | !is.null(data$model$ratio)) # Transformed
      hdata$rate <- list(InvBoxCox(hdata$rate[[1]],data$lambda))
    if(type=="cohort")
    {
      hdata$year <- c(hdata$year,data$year)
      hdata$rate <- list(cbind(hdata$rate[[1]],data$rate[[1]]))
    }
    names(hdata$rate) <- names(data$model)[4]
    if(!is.null(data$model$pop))
    {
      hdata$pop = list(data$model$pop)
      names(hdata$pop) <- names(hdata$rate)
      if(type=="cohort") # Add bogus population for future years
      {
        n <- ncol(hdata$pop[[1]])
        h <- length(hdata$year)-n
        hdata$pop[[1]] <- cbind(hdata$pop[[1]],matrix(rep(hdata$pop[[1]][,n],h),nrow=nrow(hdata$pop[[1]]),ncol=h))
      }
    }
    class(hdata) <- "demogdata"
    # Fix missing values. Why are they there?
    hdata$rate[[1]][is.na(hdata$rate[[1]])] <- 1-1e-5
    if(is.null(max.age))
      max.age <- max(data$age)
    if(missing(age))
      age <- min(hdata$age)

    x <- stats::window(life.expectancy(hdata,type=type,age=age,max.age=max.age),end=max(data$model$year))
    xf <- life.expectancy(data,years=years,type=type,age=age,max.age=max.age)
    if(type=="cohort")
    {
      xf <- ts(c(stats::window(x,start=max(data$model$year)-max.age+age+1, extend=TRUE),xf),end=max(time(xf)))
      if(sum(!is.na(xf)) > 0)
        xf <- stats::na.omit(xf)
      else
        xf <- stop("Not enough data to continue")
      if(min(time(x)) > max(data$model$year)-max.age+age)
        x <- NULL#ts(NA,end=min(time(xf))-1)
      else
        x <- stats::window(x,end=max(data$model$year)-max.age+age)
    }

    out <- structure(list(x=x,mean=xf,method="FDM model"),class="forecast")
    if(is.element("lca",class(data$model)))
      out$method = "LC model"
    else if(!is.null(data$product))
      out$method = "Coherent FDM model"
    if(PI) # Compute prediction intervals
    {
      e0calc <- (!is.element("product",names(data$rate)) & !is.element("ratio",names(data$rate)) & is.null(data$model$ratio))
      if(is.null(data$product) & is.null(data$var) & is.null(data$kt.f))
        warning("Incomplete information. Possibly this is from a coherent\n  model and you need to pass the entire object.")
      else
      {
        sim <- simulate(data,nsim,adjust.model.var=FALSE,...)
        if(type=="cohort") # Add actual rates for first few years
        {
          usex <- length(x)*any(!is.na(x))
          ny <- length(data$model$year) - usex
          sim2 <- array(NA,c(dim(sim)[1],dim(sim)[2]+ny,dim(sim)[3]))
          sim2[,(ny+1):dim(sim2)[2],] <- sim
          hrates <- hdata$rate[[1]][,usex + (1:ny)]
          sim2[,1:ny,] <- array(rep(hrates,dim(sim)[2]),c(dim(sim)[1],ny,dim(sim)[3]))
          sim <- sim2
          rm(sim2)
        }
        if(e0calc)
        {
          e0sim <- matrix(NA,dim(sim)[2],dim(sim)[3])
          simdata <- data
          if(type=="cohort")
            simdata$year <- min(time(out$mean))-1 + 1:dim(sim)[2]
          for(i in 1:dim(sim)[3])
          {
            simdata$rate <- list(as.matrix(sim[,,i]))
            names(simdata$rate) <- names(data$rate)[1]
            e0sim[,i] <- life.expectancy(simdata,type=type,age=age,max.age=max.age)
          }
          e0sim <- e0sim[1:length(xf), , drop = FALSE]
          if(is.element("lca",class(data$model)))
            out$level <- data$kt.f$level
          else
            out$level <- data$coeff[[1]]$level
          out$lower <- ts(apply(e0sim,1,quantile,prob=0.5 - out$level/200,na.rm=TRUE))
          out$upper <- ts(apply(e0sim,1,quantile,prob=0.5 + out$level/200,na.rm=TRUE))
          stats::tsp(out$lower) <- stats::tsp(out$upper) <- stats::tsp(out$mean)
        }
        out$sim <- sim
      }
    }
    return(out)
  }
  else if(is.element("fmforecast2",class(data)))
  {
    if(data[[1]]$type != "mortality")
      stop("data not a mortality object")
    if(is.null(series))
      series <- names(data)[1]
    if(is.element("product",names(data))) # Assume coherent model
    {
      if(missing(age))
        age <- min(data[["product"]]$age)
      if(is.null(max.age))
        max.age <- max(data[["product"]]$age)
      if(series=="total")
      {
        # Compute forecast total mortality rates
        # Assume sex ratios of populations stay the same as last k years
        ny <- length(data[["product"]]$model$year)
        h <- length(data[["product"]]$year)
        k <- 5
        sex.ratio <- (data$female$model$pop / data$male$model$pop)[,ny-(1:k)+1]
        sex.ratio <- apply(sex.ratio, 1, median)
        frate <- data$female$rate$female
        mrate <- data$male$rate$male
        totalrate <- frate/(1+1/sex.ratio) + mrate/(1+sex.ratio)
        # Construct demogdata object with estimated total rates
        # Use last year of population as approximation
        total <- demogdata(totalrate,
                   pop=matrix(data$female$model$pop[,ny] +
                        data$male$model$pop[,ny],
                              nrow=max.age+1,ncol=h),
                   ages=data[["product"]]$age,
                   years=data[["product"]]$year,
                   type="mortality",
                   label=data$product$model$label,
                   name="total",
                   lambda=0)
        # Compute total life expectancy
        out <- flife.expectancy(total, PI=FALSE, age=age, max.age=max.age, type=type)
        if(PI)
        {
          # Create forecast object
          if(is.element("ts",class(out)))
          {
            out <- structure(list(mean=out, method="Coherent FDM model"), class='forecast')
            frate <- exp(data$female$model$female)
            mrate <- exp(data$male$model$male)
            htotalrate <- frate/(1+1/sex.ratio) + mrate/(1+sex.ratio)
            historictotal <- demogdata(htotalrate,
                   pop=data$female$model$pop + data$male$model$pop,
                   ages=data$female$model$age,
                   years=data$female$model$year,
                   type="mortality",
                   label=data$product$model$label,
                   name="total",
                   lambda=0)
            out$x <- life.expectancy(historictotal)
          }
          # Generate simulated products and ratios
          prodsim <- flife.expectancy(data$product,nsim=nsim,PI=TRUE,age=age,max.age=max.age,type=type)
          data$ratio[["female"]]$model$ratio <- TRUE # To avoid PI calculation on flife.expectancy
          ratiosim <- flife.expectancy(data$ratio[["female"]],nsim=nsim,PI=TRUE,age=age,max.age=max.age,type=type)
          # Simulated future rates
          fsim <- prodsim$sim * ratiosim$sim
          msim <- prodsim$sim / ratiosim$sim
          sim <- fsim/(1+1/sex.ratio) + msim/(1+sex.ratio)
          dimsim <- dim(sim)
          simdata <- total
          nnn <- dim(total$rate[[1]])
          useyears <- (1:nnn[2]) + (dimsim[2]-nnn[2])
          e0sim <- matrix(NA,nnn[2],dimsim[3])
          for(i in 1:dimsim[3])
          {
            simdata$rate <- list(as.matrix(sim[,useyears,i]))
            names(simdata$rate) <- names(total$rate)[1]
            fl <- flife.expectancy(simdata,type=type,age=age,max.age=max.age)
            if(inherits(fl, "ts"))
              e0sim[,i] <- fl
            else if(inherits(fl, "forecast"))
              e0sim[,i] <- fl$mean
            else
              stop("No idea what's going on here.")
          }
          out$level <- data$product$coeff[[1]]$level
          out$lower <- ts(apply(e0sim,1,quantile,prob=0.5 - out$level/200))
          out$upper <- ts(apply(e0sim,1,quantile,prob=0.5 + out$level/200))
          stats::tsp(out$lower) <- stats::tsp(out$upper) <- stats::tsp(out$mean)
        }
      }
      else
      {
        if(missing(age))
          age <- min(data[[series]]$age)
        if(is.null(max.age))
          max.age <- max(data[[series]]$age)
        out <- flife.expectancy(data[[series]],PI=FALSE,age=age,max.age=max.age,type=type)
        out$method <- "Coherent FDM model"
        if(PI)
        {
          # Generate simulated products and ratios
          prodsim <- flife.expectancy(data$product,nsim=nsim,PI=TRUE,age=age,max.age=max.age,type=type)
          data$ratio[[series]]$model$ratio <- TRUE # To avoid PI calculation on flife.expectancy
          ratiosim <- flife.expectancy(data$ratio[[series]],nsim=nsim,PI=TRUE,age=age,max.age=max.age,type=type)
          # Simulated future rates
          sim <- prodsim$sim * ratiosim$sim
          dimsim <- dim(sim)
          simdata <- data[[series]]
          nnn <- dim(data[[series]]$rate[[1]])
          useyears <- (1:nnn[2]) + (dimsim[2]-nnn[2])
          e0sim <- matrix(NA,nnn[2],dimsim[3])
          for(i in 1:dim(sim)[3])
          {
            simdata$rate <- list(as.matrix(sim[,useyears,i]))
            names(simdata$rate) <- names(data[[series]]$rate)[1]
            fl <- flife.expectancy(simdata,type=type,age=age,max.age=max.age)
            if(inherits(fl, "ts"))
              e0sim[,i] <- fl
            else if(inherits(fl, "forecast"))
              e0sim[,i] <- fl$mean
            else
              stop("No idea what's going on here.")
          }
          #browser()
          out$level <- data$product$coeff[[1]]$level
          out$lower <- ts(apply(e0sim,1,quantile,prob=0.5 - out$level/200))
          out$upper <- ts(apply(e0sim,1,quantile,prob=0.5 + out$level/200))
          stats::tsp(out$lower) <- stats::tsp(out$upper) <- stats::tsp(out$mean)
        }
      }
    }
    else
      out <- flife.expectancy(data[[series]],PI=PI,nsim=nsim,max.age=max.age,type=type,age=age)
    return(out)
  }
  else
  {
    if(!is.element("demogdata",class(data)))
      stop("data must be a demogdata object")
    if(data$type != "mortality")
      stop("data must be a mortality object")
    if(is.null(series))
      series <- names(data$rate)[1]
    if(missing(age))
      age <- min(data$age)
    return(life.expectancy(data,series=series,years=years,type=type,age=age,max.age=max.age))
  }
}

#' @rdname life.expectancy
#' @export
e0 <- function(data, series=NULL, years=data$year,
    type=c("period","cohort"), max.age=NULL,PI=FALSE, nsim=500, ...)
{
  flife.expectancy(data, series=series, years=years,age=0,
    type=type, max.age=max.age,PI=PI,nsim=nsim,...)
}
robjhyndman/demography documentation built on July 31, 2023, 1:17 a.m.