| Title: | Bayesian Mortality Modelling with 'Stan' |
|---|---|
| Description: | Implementation of popular mortality models using the 'rstan' package, which provides the R interface to the 'Stan' C++ library for Bayesian estimation. The package supports well-known models proposed in the actuarial and demographic literature including the Lee-Carter (1992) <doi:10.1080/01621459.1992.10475265> and the Cairns-Blake-Dowd (2006) <doi:10.1111/j.1539-6975.2006.00195.x> models. By a simple call, the user inputs deaths and exposures and the package outputs the MCMC simulations for each parameter, the log likelihoods and predictions. Moreover, the package includes tools for model selection and Bayesian model averaging by leave future-out validation. |
| Authors: | Karim Barigou [aut, cre]
|
| Maintainer: | Karim Barigou <[email protected]> |
| License: | GPL-3 |
| Version: | 1.2.0 |
| Built: | 2026-10-29 10:46:39 UTC |
| Source: | https://github.com/cran/StanMoMo |
The StanMoMo package performs Bayesian Mortality Modeling with Stan for a variety of popular mortality models. The current package supports the Lee-Carter model, the Renshaw-Haberman model, the Age-Period-Cohort model, the Cairns-Blake-Dowd model and the M6 model. By a simple call, the user inputs deaths and exposures and the package outputs the MCMC simulations for each parameter, the log likelihoods and predictions. Moreover, the package includes tools for model selection and Bayesian model averaging by leave-future-out validation.
Stan Development Team (2020). RStan: the R interface to Stan. R package version 2.19.3. https://mc-stan.org
Fit and Forecast Bayesian APC model. The model can be fitted with a Poisson or Negative-Binomial distribution. The function outputs posteriors distributions for each parameter, predicted death rates and log-likelihoods.
apc_stan( death, exposure, forecast, validation = 0, family = c("poisson", "nb"), ... )apc_stan( death, exposure, forecast, validation = 0, family = c("poisson", "nb"), ... )
death |
Matrix of deaths. |
exposure |
Matrix of exposures. |
forecast |
Number of years to forecast. |
validation |
Number of years for validation. |
family |
specifies the random component of the mortality model. |
... |
Arguments passed to |
The created model is either a log-Poisson or a log-Negative-Binomial version of the APC model:
or
with
To ensure the identifiability of th model, we impose
where represents the most recent cohort in the data.
For the priors, we assume that
For the period term, similar to the LC model, we consider a random walk with drift:
with the following hyperparameters assumptions: .
For the cohort term, we consider a second order autoregressive process (AR(2)):
To close the model specification, we impose some vague priors assumptions on the hyperparameters:
An object of class stanfit returned by rstan::sampling
Cairns, A. J. G., Blake, D., Dowd, K., Coughlan, G. D., Epstein, D., Ong, A., & Balevich, I. (2009). A quantitative comparison of stochastic mortality models using data from England and Wales and the United States. North American Actuarial Journal, 13(1), 1-35.
#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model ages.fit<-70:90 years.fit<-1990:2010 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitAPC=apc_stan(death = deathFR,exposure=exposureFR, forecast = 5, family = "poisson", iter=iterations,chains=1)#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model ages.fit<-70:90 years.fit<-1990:2010 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitAPC=apc_stan(death = deathFR,exposure=exposureFR, forecast = 5, family = "poisson", iter=iterations,chains=1)
The function generates boxplots for the posterior distribution of the main parameters
boxplot_post_dist(stan_fit, parm_name, ages, years)boxplot_post_dist(stan_fit, parm_name, ages, years)
stan_fit |
stan fit object |
parm_name |
string to indicate the name of the parameter, to choose from c('a', 'b', 'g', 'k', 'k2', 'phi') |
ages |
range of ages |
years |
range of years |
Posterior distribution shown as boxplots
years <- 1990:2017 ages <- 50:90 cohorts <- sort(unique(as.vector(sapply(years, function(year) year - ages)))) death <- FRMaleData$Dxt[formatC(ages),formatC(years)] exposure <- FRMaleData$Ext[formatC(ages),formatC(years)] iterations<-50 # Toy example, consider at least 2000 iterations stan_fit <- fit_mo_mo("m6", death , exposure, ages, 0, 5, "nb", 1, 4, log_marg = FALSE,iter=iterations) boxplot_post_dist(stan_fit, "k", ages, years) boxplot_post_dist(stan_fit, "g", ages, years)years <- 1990:2017 ages <- 50:90 cohorts <- sort(unique(as.vector(sapply(years, function(year) year - ages)))) death <- FRMaleData$Dxt[formatC(ages),formatC(years)] exposure <- FRMaleData$Ext[formatC(ages),formatC(years)] iterations<-50 # Toy example, consider at least 2000 iterations stan_fit <- fit_mo_mo("m6", death , exposure, ages, 0, 5, "nb", 1, 4, log_marg = FALSE,iter=iterations) boxplot_post_dist(stan_fit, "k", ages, years) boxplot_post_dist(stan_fit, "g", ages, years)
Fit and Forecast Bayesian CBD model. The model can be fitted with a Poisson or Negative-Binomial distribution. The function outputs posteriors distributions for each parameter, predicted death rates and log-likelihoods.
cbd_stan( death, exposure, age, forecast, validation = 0, family = c("poisson", "nb"), ... )cbd_stan( death, exposure, age, forecast, validation = 0, family = c("poisson", "nb"), ... )
death |
Matrix of deaths. |
exposure |
Matrix of exposures. |
age |
Vector of ages. |
forecast |
Number of years to forecast. |
validation |
Number of years for validation. |
family |
specifies the random component of the mortality model. |
... |
Arguments passed to |
The created model is either a log-Poisson or a log-Negative-Binomial version of the CBD model:
or
with
where is the average age in the data.
For the period terms, we consider a multivariate random walk with drift:
with normal priors: .
The variance-covariance matrix of the error term is defined by
where the variance coefficients have independent exponential priors:
and the correlation parameter has a uniform prior: .
As for the other models, the overdispersion parameter has a prior distribution given by
An object of class stanfit returned by rstan::sampling
Cairns, A. J. G., Blake, D., & Dowd, K. (2006). A Two-Factor Model for Stochastic Mortality with Parameter Uncertainty: Theory and Calibration. Journal of Risk and Insurance, 73(4), 687-718.
#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-NB model ages.fit<-50:90 years.fit<-1970:2017 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitCBD=cbd_stan(death = deathFR,exposure=exposureFR, age=ages.fit, forecast = 10, family = "poisson",iter=iterations,chains=1)#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-NB model ages.fit<-50:90 years.fit<-1970:2017 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitCBD=cbd_stan(death = deathFR,exposure=exposureFR, age=ages.fit, forecast = 10, family = "poisson",iter=iterations,chains=1)
Compute the model evidence/marginal likelihood via bridge sampling (or via the harmonic mean estimator if bridge sampling fails)
compute_weights_BMA(stan_fits, mortality_models)compute_weights_BMA(stan_fits, mortality_models)
stan_fits |
list of Stan model fits where the marginal likelihood was computed via bridge sampling |
mortality_models |
vector of mortality models names |
data frame with model evidence for BMA
Function to get the a posterior means of the parameters based on a stanfit object
extract_map(stan_fit)extract_map(stan_fit)
stan_fit |
a stanfit object |
named list with the point estimates of the parameters
Wrapper function to fit and forecast mortality models
fit_mo_mo( mortality_model = "lc", death, exposure, ages = 50:90, validation = 0, forecast = 1, family = "nb", chains = 1, cores = 4, log_marg = FALSE, iter = 2000 )fit_mo_mo( mortality_model = "lc", death, exposure, ages = 50:90, validation = 0, forecast = 1, family = "nb", chains = 1, cores = 4, log_marg = FALSE, iter = 2000 )
mortality_model |
name of the mortality model |
death |
death matrix |
exposure |
exposure matrix |
ages |
vector of ages |
validation |
size of the validation set |
forecast |
number of calendar years to be forecast |
family |
underlying count distribution |
chains |
number of Markov chains |
cores |
number of cores used |
log_marg |
Do we compute the marginal likelihood or not? |
iter |
Length of the Markov chain trajectory |
a stanfit object
years <- 1990:2017 ages <- 50:90 cohorts <- sort(unique(as.vector(sapply(years, function(year) year - ages)))) death <- FRMaleData$Dxt[formatC(ages),formatC(years)] exposure <- FRMaleData$Ext[formatC(ages),formatC(years)] stan_fit <- fit_mo_mo("m6", death , exposure, ages, 0, 5, "nb", 1, 4, log_marg = FALSE,iter=50) boxplot_post_dist(stan_fit, "k", ages, years) boxplot_post_dist(stan_fit, "g", ages, years)years <- 1990:2017 ages <- 50:90 cohorts <- sort(unique(as.vector(sapply(years, function(year) year - ages)))) death <- FRMaleData$Dxt[formatC(ages),formatC(years)] exposure <- FRMaleData$Ext[formatC(ages),formatC(years)] stan_fit <- fit_mo_mo("m6", death , exposure, ages, 0, 5, "nb", 1, 4, log_marg = FALSE,iter=50) boxplot_post_dist(stan_fit, "k", ages, years) boxplot_post_dist(stan_fit, "g", ages, years)
List containing deaths and exposures of French Male for ages 0-110 and years 1816-2017.
FRMaleDataFRMaleData
A list of 2 matrices
Matrix of deaths, with 111 rows (ages) and 202 columns (years)
Matrix of exposures, with 111 rows (ages) and 202 columns (years)
Human Mortality Database https://www.mortality.org/
Human Mortality Database (2011). University of California, Berkeley (USA), and Max Planck Institute for Demographic Research (Germany). Available at https://www.mortality.org/
Fit and Forecast Bayesian Lee-Carter model. The model can be fitted with a Poisson or Negative-Binomial distribution. The function outputs posteriors distributions for each parameter, predicted death rates and log-likelihoods.
lc_stan( death, exposure, forecast, validation = 0, family = c("poisson", "nb"), ... )lc_stan( death, exposure, forecast, validation = 0, family = c("poisson", "nb"), ... )
death |
Matrix of deaths. |
exposure |
Matrix of exposures. |
forecast |
Number of years to forecast. |
validation |
Number of years for validation. |
family |
specifies the random component of the mortality model. |
... |
Arguments passed to |
The created model is either a log-Poisson or a log-Negative-Binomial version of the Lee-Carter model:
or
with
To ensure the identifiability of th model, we impose
For the priors, the model chooses relatively wide priors:
For the period term, we consider a first order autoregressive process (AR(1)) with linear trend:
with .
An object of class stanfit returned by rstan::sampling.
Lee, R. D., & Carter, L. R. (1992). Modeling and forecasting U.S. mortality. Journal of the American Statistical Association, 87(419), 659-671.
#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model ages.fit<-50:90 years.fit<-1970:2017 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitLC=lc_stan(death = deathFR,exposure=exposureFR, forecast = 10, family = "poisson",iter=iterations,chains=1)#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model ages.fit<-50:90 years.fit<-1970:2017 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitLC=lc_stan(death = deathFR,exposure=exposureFR, forecast = 10, family = "poisson",iter=iterations,chains=1)
Fit and Forecast Bayesian M6 model (CBD with cohort effect) introduced in Cairns et al (2009). The model can be fitted with a Poisson or Negative-Binomial distribution. The function outputs posteriors distributions for each parameter, predicted death rates and log-likelihoods.
m6_stan( death, exposure, forecast, age, validation = 0, family = c("poisson", "nb"), ... )m6_stan( death, exposure, forecast, age, validation = 0, family = c("poisson", "nb"), ... )
death |
Matrix of deaths. |
exposure |
Matrix of exposures. |
forecast |
Number of years to forecast. |
age |
Vector of ages. |
validation |
Number of years for validation. |
family |
specifies the random component of the mortality model. |
... |
Arguments passed to |
The created model is either a log-Poisson or a log-Negative-Binomial version of the M6 model:
or
with
where is the average age in the data.
To ensure the identifiability of th model, we impose
where represents the most recent cohort in the data.
For the period terms, we consider a multivariate random walk with drift:
with normal priors: .
The variance-covariance matrix of the error term is defined by
where the variance coefficients have independent exponential priors:
and the correlation parameter has a uniform prior: .
As for the other models, the overdispersion parameter has a prior distribution given by
For the cohort term, we consider a second order autoregressive process (AR(2)):
To close the model specification, we impose some vague priors assumptions on the hyperparameters:
An object of class stanfit returned by rstan::sampling.
Cairns, A. J. G., Blake, D., Dowd, K., Coughlan, G. D., Epstein, D., Ong, A., & Balevich, I. (2009). A quantitative comparison of stochastic mortality models using data from England and Wales and the United States. North American Actuarial Journal, 13(1), 1-35.
#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model ages.fit<-70:90 years.fit<-1990:2010 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitM6=m6_stan(death = deathFR,exposure=exposureFR, age=ages.fit,forecast = 5, family = "poisson",iter=iterations,chains=1)#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model ages.fit<-70:90 years.fit<-1990:2010 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitM6=m6_stan(death = deathFR,exposure=exposureFR, age=ages.fit,forecast = 5, family = "poisson",iter=iterations,chains=1)
Mortality Model averaging via stacking of predictive distributions and Pseudo-BMA weighting. Based on Yao et al. (2018) but adapted by Barigou et al. (2021) for mortality forecasting.
mortality_weights(X)mortality_weights(X)
X |
A list of stanfit objects. |
Mortality model averaging via stacking of predictive distributions or pseudo-BMA weighting. Both approaches were proposed in Yao et al. (2018) based leave-one-out cross-validation which is not suited for forecasting. Barigou et al. (2021) adapted both approaches based on leave-future-out validation which is more appropriate for mortality forecasting.
The stacking method combines all models by maximizing the leave-future-out predictive density of the combination distribution. That is, it finds the optimal linear combining weights for maximizing the leave-future-out log score.
The pseudo-BMA method finds the relative weights proportional to the expected log predictive density of each model.
Similar to Yao et al. (2018), we recommend stacking for averaging predictive distributions as pseudo-BMA tends to select only one model.
A matrix containing one weight for each model and each approach.
Yao, Y., Vehtari, A., Simpson, D., & Gelman, A. (2018). Using stacking to average Bayesian predictive distributions (with discussion). Bayesian Analysis, 13(3), 917-1007.
Barigou K., Goffard P-O., Loisel S., Salhi Y. (2021). Bayesian model averaging for mortality forecasting using leave-future-out validation. arXiv preprint arXiv:2103.15434.
#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model #where the 10 last years are held out for validation. We search for the model weights between #the Lee-Carter model and the RH model (Lee-Carter with cohort effect). ages.fit<-50:90 years.fit<-1970:2017 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-1000 # Toy example, consider at least 2000 iterations fitLC=lc_stan(death = deathFR,exposure=exposureFR, forecast = 10, validation=10, family = "poisson",iter=iterations,chains=1) fitRH=rh_stan(death = deathFR,exposure=exposureFR, forecast = 10, validation=10, family = "poisson",iter=iterations,chains=1) model_weights<-mortality_weights(list(fitLC,fitRH))#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model #where the 10 last years are held out for validation. We search for the model weights between #the Lee-Carter model and the RH model (Lee-Carter with cohort effect). ages.fit<-50:90 years.fit<-1970:2017 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-1000 # Toy example, consider at least 2000 iterations fitLC=lc_stan(death = deathFR,exposure=exposureFR, forecast = 10, validation=10, family = "poisson",iter=iterations,chains=1) fitRH=rh_stan(death = deathFR,exposure=exposureFR, forecast = 10, validation=10, family = "poisson",iter=iterations,chains=1) model_weights<-mortality_weights(list(fitLC,fitRH))
Fit and Forecast Bayesian Renshaw-Haberman model (Lee-Carter with cohort effect) introduced in Renshaw and Haberman (2006). The model can be fitted with a Poisson or Negative-Binomial distribution. The function outputs posteriors distributions for each parameter, predicted death rates and log-likelihoods.
rh_stan( death, exposure, forecast, validation = 0, family = c("poisson", "nb"), ... )rh_stan( death, exposure, forecast, validation = 0, family = c("poisson", "nb"), ... )
death |
Matrix of deaths. |
exposure |
Matrix of exposures. |
forecast |
Number of years to forecast. |
validation |
Number of years for validation. |
family |
specifies the random component of the mortality model. |
... |
Arguments passed to |
The created model is either a log-Poisson or a log-Negative-Binomial version of the Renshaw-Haberman model:
or
with
To ensure the identifiability of th model, we impose
where represents the most recent cohort in the data.
For the priors, the model chooses wide priors:
For the period term, we consider the standard random walk with drift:
with .
For the cohort term, we consider a second order autoregressive process (AR(2)):
To close the model specification, we impose some vague priors assumptions on the hyperparameters:
An object of class stanfit returned by rstan::sampling.
Renshaw, A. E., & Haberman, S. (2006). A cohort-based extension to the Lee-Carter model for mortality reduction factors. Insurance: Mathematics and Economics, 38(3), 556-570.
#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model ages.fit<-70:90 years.fit<-1990:2010 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitRH=rh_stan(death = deathFR,exposure=exposureFR, forecast = 5, family = "poisson", iter=iterations,chains=1)#10-year forecasts for French data for ages 50-90 and years 1970-2017 with a log-Poisson model ages.fit<-70:90 years.fit<-1990:2010 deathFR<-FRMaleData$Dxt[formatC(ages.fit),formatC(years.fit)] exposureFR<-FRMaleData$Ext[formatC(ages.fit),formatC(years.fit)] iterations<-50 # Toy example, consider at least 2000 iterations fitRH=rh_stan(death = deathFR,exposure=exposureFR, forecast = 5, family = "poisson", iter=iterations,chains=1)
Simulation of death counts from the Age-Period-Cohort mortality model
sim_death_apc(a, k, g, phi, years, ages, exposure)sim_death_apc(a, k, g, phi, years, ages, exposure)
a |
vector of age component |
k |
vector of period component |
g |
vector of cohort component |
phi |
dispersion parameter |
years |
vector of calendar years |
ages |
vectors of ages |
exposure |
matrix of exposure data |
matrix of death count
Simulation of death counts from the CBD model
sim_death_cbd(k, k2, phi, years, ages, exposure)sim_death_cbd(k, k2, phi, years, ages, exposure)
k |
first vector of period component |
k2 |
second vector of period component |
phi |
dispersion parameter |
years |
vector of calendar years |
ages |
vectors of ages |
exposure |
matrix of exposure data |
matrix of death count
Simulation of death counts from the Lee-Carter mortality model
sim_death_lc(a, b, k, phi, exposure)sim_death_lc(a, b, k, phi, exposure)
a |
vector of age component |
b |
vector of age/year component |
k |
vector of year component |
phi |
dispersion parameter |
exposure |
matrix of exposure data |
matrix of death count
Simulation of death counts from the M6 model
sim_death_m6(k, k2, g, phi, years, ages, exposure)sim_death_m6(k, k2, g, phi, years, ages, exposure)
k |
first vector of period component |
k2 |
second vector of period component |
g |
vector of cohort component |
phi |
dispersion parameter |
years |
vector of calendar years |
ages |
vectors of ages |
exposure |
matrix of exposure data |
matrix of death count
Simulation of death counts from a hybrid model that averages the mortality rates from the cbd and rh models
sim_death_mix_cbd_rh(params_cbd, params_rh, years, ages, exposure, q)sim_death_mix_cbd_rh(params_cbd, params_rh, years, ages, exposure, q)
params_cbd |
named list that contains the parameters of the cbd model |
params_rh |
named list that contains the parameters of the rh model |
years |
vector of calendar year |
ages |
vector of ages |
exposure |
matrix of exposure data |
q |
mixing parameter (0 <- rh, 1 <- cbd) |
matrix of death count
Simulation of death counts from the Renshaw-Haberman mortality model
sim_death_rh(a, b, k, g, phi, years, ages, exposure)sim_death_rh(a, b, k, g, phi, years, ages, exposure)
a |
vector of age component |
b |
vector of age/year component |
k |
vector of period component |
g |
vector of cohort component |
phi |
dispersion parameter |
years |
vector of calendar years |
ages |
vectors of ages |
exposure |
matrix of exposure data |
matrix of death count
Simulation of mortality data from various models
sim_mortality_data(a, k, k2, b, g, phi, years, ages, exposure, mortality_model)sim_mortality_data(a, k, k2, b, g, phi, years, ages, exposure, mortality_model)
a |
vector of age component |
k |
first vector of time component |
k2 |
second vector of time component |
b |
vector of age/time component |
g |
vector of cohort component |
phi |
dispersion parameter |
years |
vector of calendar year |
ages |
vector of ages |
exposure |
matrix of exposure |
mortality_model |
name of the mortality model that we simulate from |
matrix of death counts