31  Bayesian Mortality Modeling

Author

Alec Loudenback

“After a year of intense mental struggle, however, [Arthur Bailey] realized to his consternation that actuarial sledgehammering worked. He even preferred [the Bayesian underpinnings of credibility theory] to the elegance of frequentism. He positively liked formulae that described ‘actual data. . . . I realized that the hard-shelled underwriters were recognizing certain facts of life neglected by the statistical theorists.’ He wanted to give more weight to a large volume of data than to the frequentists’ small sample; doing so felt surprisingly ‘logical and reasonable.’ He concluded that only a ‘suicidal’ actuary would use Fisher’s method of maximum likelihood, which assigned a zero probability to nonevents.” - Sharon Bertsch McGrayne, Excerpt From The Theory That Would Not Die

31.1 Chapter Overview

An example of using a Bayesian MCMC approach to fitting a mortality curve to sample data, with multi-level models and censored data.

31.2 Generating fake data

The problem of interest is to look at mortality rates, which are given in terms of exposures (whether or not a life experienced a death in a given year).

We’ll grab some example rates from an insurance table, which has a “selection” component: When someone enters observation, say at age 50, their mortality is path dependent (so for someone who started being observed at 50 will have a different risk/mortality rate at age 55 than someone who started being observed at 45).

Addtionally, there may be additional groups of interest, such as:

  • high/medium/low risk classification
  • sex
  • group (e.g. company, data source, etc.)
  • type of insurance product offered

The example data will start with only the risk classification above.

using MortalityTables
using Turing
using DataFramesMeta
using MCMCChains
using LinearAlgebra
using CairoMakie
using StatsBase
n = 10_000
inforce = map(1:n) do i
    (
        issue_age=rand(30:70),
        risk_level=rand(1:3),
    )

end
10000-element Vector{@NamedTuple{issue_age::Int64, risk_level::Int64}}:
 (issue_age = 55, risk_level = 2)
 (issue_age = 34, risk_level = 2)
 (issue_age = 36, risk_level = 1)
 (issue_age = 46, risk_level = 3)
 (issue_age = 42, risk_level = 2)
 (issue_age = 57, risk_level = 2)
 (issue_age = 37, risk_level = 2)
 (issue_age = 30, risk_level = 1)
 (issue_age = 56, risk_level = 1)
 (issue_age = 69, risk_level = 3)
 ⋮
 (issue_age = 65, risk_level = 1)
 (issue_age = 67, risk_level = 3)
 (issue_age = 69, risk_level = 2)
 (issue_age = 61, risk_level = 3)
 (issue_age = 31, risk_level = 3)
 (issue_age = 35, risk_level = 1)
 (issue_age = 47, risk_level = 2)
 (issue_age = 46, risk_level = 3)
 (issue_age = 30, risk_level = 1)
base_table = MortalityTables.table("2001 VBT Residual Standard Select and Ultimate - Male Nonsmoker, ANB")

function tabular_mortality(params, issue_age, att_age, risk_level)
    q = params.ultimate[att_age]
    if risk_level == 1
        q *= 0.7
    elseif risk_level == 2
        q = q
    else
        q *= 1.5
    end
end
tabular_mortality (generic function with 1 method)
function model_outcomes(inforce, assumption, assumption_params; n_years=5)

    outcomes = map(inforce) do pol
        alive = 1
        sim = map(1:n_years) do t
            att_age = pol.issue_age + t - 1
            q = assumption(
                assumption_params,
                pol.issue_age,
                att_age,
                pol.risk_level
            )
            if rand() < q
                out = (att_age=att_age, exposures=alive, death=1)
                alive = 0
                out
            else
                (att_age=att_age, exposures=alive, death=0)
            end
        end
        filter!(x -> x.exposures == 1, sim)

    end


    df = DataFrame(inforce)

    df.outcomes = outcomes
    df = flatten(df, :outcomes)

    df.att_age = [x.att_age for x in df.outcomes]
    df.death = [x.death for x in df.outcomes]
    df.exposures = [x.exposures for x in df.outcomes]
    select!(df, Not(:outcomes))


end

exposures = model_outcomes(inforce, tabular_mortality, base_table)
data = combine(groupby(exposures, [:issue_age, :att_age])) do subdf
    (exposures=nrow(subdf),
        deaths=sum(subdf.death),
        fraction=sum(subdf.death) / nrow(subdf))
end


data2 = combine(groupby(exposures, [:issue_age, :att_age, :risk_level])) do subdf
    (exposures=nrow(subdf),
        deaths=sum(subdf.death),
        fraction=sum(subdf.death) / nrow(subdf))
end
615×6 DataFrame
590 rows omitted
Row issue_age att_age risk_level exposures deaths fraction
Int64 Int64 Int64 Int64 Int64 Float64
1 30 30 1 83 0 0.0
2 30 30 2 71 1 0.0140845
3 30 30 3 65 0 0.0
4 30 31 1 83 0 0.0
5 30 31 2 70 0 0.0
6 30 31 3 65 0 0.0
7 30 32 1 83 0 0.0
8 30 32 2 70 0 0.0
9 30 32 3 65 0 0.0
10 30 33 1 83 0 0.0
11 30 33 2 70 1 0.0142857
12 30 33 3 65 0 0.0
13 30 34 1 83 1 0.0120482
604 70 71 1 80 0 0.0
605 70 71 2 74 5 0.0675676
606 70 71 3 60 1 0.0166667
607 70 72 1 80 3 0.0375
608 70 72 2 69 2 0.0289855
609 70 72 3 59 1 0.0169492
610 70 73 1 77 3 0.038961
611 70 73 2 67 3 0.0447761
612 70 73 3 58 6 0.103448
613 70 74 1 74 2 0.027027
614 70 74 2 64 3 0.046875
615 70 74 3 52 2 0.0384615

31.3 1: A single binomial parameter model

Estimate \(q\), the average mortality rate, not accounting for any variation within the population/sample. Our model is defines as:

\[ q ~ Beta(1,1) p(\text{death}) ~ \text{Binomial}(q) \]

@model function mortality(data, deaths)
    q ~ Beta(1, 1)
    for i = 1:nrow(data)
        deaths[i] ~ Binomial(data.exposures[i], q)
    end
end

m1 = mortality(data, data.deaths)
DynamicPPL.Model{typeof(mortality), (:data, :deaths), (), (), Tuple{DataFrame, Vector{Int64}}, Tuple{}, DynamicPPL.DefaultContext}(mortality, (data = 205×5 DataFrame
 Row  issue_age  att_age  exposures  deaths  fraction    Int64      Int64    Int64      Int64   Float64    
─────┼───────────────────────────────────────────────────
   1 │        30       30        219       1  0.00456621
   2 │        30       31        218       0  0.0
   3 │        30       32        218       0  0.0
   4 │        30       33        218       1  0.00458716
   5 │        30       34        217       1  0.00460829
   6 │        31       31        268       1  0.00373134
   7 │        31       32        267       0  0.0
   8 │        31       33        267       0  0.0
  ⋮  │     ⋮         ⋮         ⋮        ⋮         ⋮
 199 │        69       72        236       9  0.0381356
 200 │        69       73        227       6  0.0264317
 201 │        70       70        222       8  0.036036
 202 │        70       71        214       6  0.0280374
 203 │        70       72        208       6  0.0288462
 204 │        70       73        202      12  0.0594059
 205 │        70       74        190       7  0.0368421
                                         190 rows omitted, deaths = [1, 0, 0, 1, 1, 1, 0, 0, 1, 1  …  3, 9, 3, 9, 6, 8, 6, 6, 12, 7]), NamedTuple(), DynamicPPL.DefaultContext())

31.3.1 Sampling from the posterior

We use a No-U-Turn-Sampler (NUTS) technique to sample multiple chains at once:

num_chains = 4
chain = sample(m1, NUTS(), MCMCThreads(), 400, num_chains)
Chains MCMC chain (400×13×4 Array{Float64, 3}):

Iterations        = 201:1:600
Number of chains  = 4
Samples per chain = 400
Wall duration     = 2.07 seconds
Compute duration  = 8.23 seconds
parameters        = q
internals         = lp, n_steps, is_accept, acceptance_rate, log_density, hamiltonian_energy, hamiltonian_energy_error, max_hamiltonian_energy_error, tree_depth, numerical_error, step_size, nom_step_size

Summary Statistics
  parameters      mean       std      mcse   ess_bulk   ess_tail      rhat   e     Symbol   Float64   Float64   Float64    Float64    Float64   Float64     ⋯

           q    0.0086    0.0004    0.0000   703.8364   984.7212    1.0007     ⋯
                                                                1 column omitted

Quantiles
  parameters      2.5%     25.0%     50.0%     75.0%     97.5% 
      Symbol   Float64   Float64   Float64   Float64   Float64 

           q    0.0077    0.0083    0.0085    0.0088    0.0094

Here, we have asked for the outcomes to be modeled via a single parameter for the population. We see that the posterior distribution of \(q\) is very close to the overall population mortality rate:

# Posterior mean of q should be close to the pooled fraction
sum(data.deaths) / sum(data.exposures)
0.008533636751528944

However, We can see that the sampling of possible posterior parameters doesn’t really fit the data very well since our model was so simplified. The lines represent the posterior binomial probability.

This is saying that for the observed data, if there really is just a single probability p that governs the true process that came up with the data, there’s a pretty narrow range of values it could possibly be:

let
    data_weight = sqrt.(data.exposures) / 2
    f = Figure(title="Parametric Bayesian Mortality"
    )
    ax = Axis(f[1, 1],
        xlabel="age",
        ylabel="mortality rate",
        limits=(nothing, nothing, -0.01, 0.10),
    )
    scatter!(ax,
        data.att_age,
        data.fraction,
        markersize=data_weight,
        color=(:blue, 0.5),
        label="Experience data point (size indicates relative exposure quantity)",)

    # show n samples from the posterior plotted on the graph
    n = 300
    ages = sort!(unique(data.att_age))

    q_posterior = sample(chain, n)[:q]


    for i in 1:n

        hlines!(ax, [q_posterior[i]], color=(:grey, 0.1))
    end

    sim05 = Float64[]
    sim95 = Float64[]
    for r in eachrow(data)
        outcomes = map(1:n) do i
            rand(Binomial(r.exposures, q_posterior[i]), 500)
        end
        push!(sim05, quantile(Iterators.flatten(outcomes), 0.05) / r.exposures)
        push!(sim95, quantile(Iterators.flatten(outcomes), 0.95) / r.exposures)


    end



    f
end
let
    n = 300
    q_posterior = sample(chain, n)[:q]


end
2-dimensional AxisArray{Float64,2,...} with axes:
    :iter, 1:300
    :chain, 1:1
And data, a 300×1 Matrix{Float64}:
 0.008635668654579745
 0.009236679663994952
 0.008716594908961631
 0.007848755439702043
 0.007490891781211297
 0.009035257334031129
 0.009024333614318777
 0.008055042342873787
 0.008537078772980598
 0.008356924902844105
 ⋮
 0.008517370324275316
 0.008608878593954138
 0.008199228707438794
 0.008474701957349355
 0.009068822919529871
 0.008583671659268535
 0.008039259664180927
 0.009452172148041978
 0.008982355891336963

31.4 2. Parametric model

In this example, we utilize a MakehamBeard parameterization because it’s already very similar in form to a logistic function. This is important because our desired output is a probability (ie the probability of a death at a given age), so the value must be constrained to be in the interval between zero and one.

The prior values for a,b,c, and k are chosen to constrain the hazard (mortality) rate to be between zero and one.

This isn’t an ideal parameterization (e.g. we aren’t including information about the select underwriting period), but is an example of utilizing Bayesian techniques on life experience data. ”

@model function mortality2(data, deaths)
    a ~ Exponential(0.1)
    b ~ Exponential(0.1)
    c = 0.0
    k ~ truncated(Exponential(1), 1, Inf)

    # use the variables to create a parametric mortality model
    m = MortalityTables.MakehamBeard(; a, b, c, k)

    # loop through the rows of the dataframe to let Turing observe the data 
    # and how consistent the parameters are with the data
    for i = 1:nrow(data)
        age = data.att_age[i]
        q = MortalityTables.hazard(m, age)
        deaths[i] ~ Binomial(data.exposures[i], q)
    end
end
mortality2 (generic function with 2 methods)

We combine the model with the data and sample from the posterior using a similar call as before:

m2 = mortality2(data, data.deaths)

chain2 = sample(m2, NUTS(), MCMCThreads(), 400, num_chains)
Chains MCMC chain (400×15×4 Array{Float64, 3}):

Iterations        = 201:1:600
Number of chains  = 4
Samples per chain = 400
Wall duration     = 5.67 seconds
Compute duration  = 20.99 seconds
parameters        = a, b, k
internals         = lp, n_steps, is_accept, acceptance_rate, log_density, hamiltonian_energy, hamiltonian_energy_error, max_hamiltonian_energy_error, tree_depth, numerical_error, step_size, nom_step_size

Summary Statistics
  parameters      mean       std      mcse   ess_bulk   ess_tail      rhat   e     Symbol   Float64   Float64   Float64    Float64    Float64   Float64     ⋯

           a    0.0000    0.0000    0.0000   494.0051   519.4787    1.0045     ⋯
           b    0.0912    0.0055    0.0003   485.2947   505.3110    1.0050     ⋯
           k    1.8985    0.9014    0.0326   571.9751   466.0100    1.0036     ⋯
                                                                1 column omitted

Quantiles
  parameters      2.5%     25.0%     50.0%     75.0%     97.5% 
      Symbol   Float64   Float64   Float64   Float64   Float64 

           a    0.0000    0.0000    0.0000    0.0001    0.0001
           b    0.0805    0.0875    0.0912    0.0950    0.1020
           k    1.0244    1.2390    1.6170    2.2538    4.2105

31.4.1 Plotting samples from the posterior

We can see that the sampling of possible posterior parameters fits the data well:

let
    data_weight = sqrt.(data.exposures) / 2

    p = scatter(
        data.att_age,
        data.fraction,
        markersize=data_weight,
        alpha=0.5,
        label="Experience data point (size indicates relative exposure quantity)",
        axis=(
            xlabel="age",
            limits=(nothing, nothing, -0.01, 0.10),
            ylabel="mortality rate",
            title="Parametric Bayesian Mortality"
        )
    )


    # show n samples from the posterior plotted on the graph
    n = 300
    ages = sort!(unique(data.att_age))

    for i in 1:n
        s = sample(chain2, 1)
        a = only(s[:a])
        b = only(s[:b])
        k = only(s[:k])
        c = 0
        m = MortalityTables.MakehamBeard(; a, b, c, k)
        lines!(ages, age -> MortalityTables.hazard(m, age), alpha=0.1, label="")
    end
    p
end
let
    data_weight = sqrt.(data.exposures) / 2
    f = Figure(title="Parametric Bayesian Mortality"
    )
    ax = Axis(f[1, 1],
        xlabel="age",
        ylabel="mortality rate",
        limits=(nothing, nothing, -0.01, 0.10),
    )
    scatter!(ax,
        data.att_age,
        data.fraction,
        markersize=data_weight,
        color=(:blue, 0.5),
        label="Experience data point (size indicates relative exposure quantity)",)

    # show n samples from the posterior plotted on the graph
    n = 300
    ages = sort!(unique(data.att_age))

    for i in 1:n
        s = sample(chain2, 1)
        a = only(s[:a])
        b = only(s[:b])
        k = only(s[:k])
        c = 0
        m = MortalityTables.MakehamBeard(; a, b, c, k)
        qs = MortalityTables.hazard.(m, ages)
        lines!(ax, ages, qs, color=(:grey, 0.1))
    end
    f
end

Recall that the lines are not plotting the possible outcomes of the claims rates, but the mean claims rate for the given age.

31.5 3. Multi-level model

This model extends the prior to create a multi-level model. Each risk class (risk_level) gets its own \(a\) paramater in the MakhamBeard model. The prior for \(a_i\) is determined by the hyper-parameter \(\bar{a}\).

@model function mortality3(data, deaths)
    risk_levels = length(levels(data.risk_level))
    b ~ Exponential(0.1)
    ā ~ Exponential(0.1)
    a ~ filldist(Exponential(ā), risk_levels)
    c = 0
    k ~ truncated(Exponential(1), 1, Inf)

    # use the variables to create a parametric mortality model

    # loop through the rows of the dataframe to let Turing observe the data 
    # and how consistent the parameters are with the data
    for i = 1:nrow(data)
        risk = data.risk_level[i]

        m = MortalityTables.MakehamBeard(; a=a[risk], b, c, k)
        age = data.att_age[i]
        q = MortalityTables.hazard(m, age)
        deaths[i] ~ Binomial(data.exposures[i], q)
    end
end

m3 = mortality3(data2, data2.deaths)

chain3 = sample(m3, NUTS(), 1000)

summarize(chain3)
  parameters      mean       std      mcse   ess_bulk   ess_tail      rhat   e     Symbol   Float64   Float64   Float64    Float64    Float64   Float64     ⋯

           b    0.0914    0.0058    0.0004   247.9515   284.7842    1.0040     ⋯
           ā    0.0002    0.0005    0.0000   246.3024   227.9634    1.0060     ⋯
        a[1]    0.0000    0.0000    0.0000   244.7297   366.9222    1.0036     ⋯
        a[2]    0.0000    0.0000    0.0000   259.0605   257.3560    1.0051     ⋯
        a[3]    0.0001    0.0000    0.0000   255.9665   273.8641    1.0046     ⋯
           k    2.0254    0.9568    0.0330   718.4169   621.7398    0.9995     ⋯
                                                                1 column omitted
let data = data2

    data_weight = sqrt.(data.exposures)
    color_i = data.risk_level
    cm = CairoMakie.Makie.wong_colors()

    p, ax, _ = scatter(
        data.att_age,
        data.fraction,
        markersize=data_weight,
        alpha=0.5,
        color=[(CairoMakie.Makie.wong_colors()[c], 0.7) for c in color_i],
        colormap=CairoMakie.Makie.wong_colors(),
        label="Experience data point (size indicates relative exposure quantity)",
        axis=(
            xlabel="age",
            limits=(nothing, nothing, -0.01, 0.10),
            ylabel="mortality rate",
            title="Parametric Bayesian Mortality"
        )
    )


    # show n samples from the posterior plotted on the graph
    n = 100

    ages = sort!(unique(data.att_age))
    for r in 1:3
        for i in 1:n
            s = sample(chain3, 1)
            a = only(s[Symbol("a[$r]")])
            b = only(s[:b])
            k = only(s[:k])
            c = 0
            m = MortalityTables.MakehamBeard(; a, b, c, k)
            lines!(ages, age -> MortalityTables.hazard(m, age), label="risk level $r", alpha=0.2, color=(CairoMakie.Makie.wong_colors()[r], 0.2))
        end
    end
    axislegend(ax, merge=true)
    p
end

Again, the lines are not plotting the possible outcomes of the claims rates, but the mean claims rate for the given age and risk class.

31.6 Handling non-unit exposures

The key is to use the Poisson distribution, which is a continuous approximation to the Binomial distribution:

@model function mortality4(data, deaths)
    risk_levels = length(levels(data.risk_level))
    b ~ Exponential(0.1)
    ā ~ Exponential(0.1)
    a ~ filldist(Exponential(ā), risk_levels)
    c ~ Beta(4, 18)
    k ~ truncated(Exponential(1), 1, Inf)

    # use the variables to create a parametric mortality model

    # loop through the rows of the dataframe to let Turing observe the data 
    # and how consistent the parameters are with the data
    for i = 1:nrow(data)
        risk = data.risk_level[i]

        m = MortalityTables.MakehamBeard(; a=a[risk], b, c, k)
        age = data.att_age[i]
        q = MortalityTables.hazard(m, age)
        deaths[i] ~ Poisson(data.exposures[i] * q)
    end
end

m4 = mortality4(data2, data2.deaths)

chain4 = sample(m4, NUTS(), 1000)
Chains MCMC chain (1000×19×1 Array{Float64, 3}):

Iterations        = 501:1:1500
Number of chains  = 1
Samples per chain = 1000
Wall duration     = 26.16 seconds
Compute duration  = 26.16 seconds
parameters        = b, ā, a[1], a[2], a[3], c, k
internals         = lp, n_steps, is_accept, acceptance_rate, log_density, hamiltonian_energy, hamiltonian_energy_error, max_hamiltonian_energy_error, tree_depth, numerical_error, step_size, nom_step_size

Summary Statistics
  parameters      mean       std      mcse   ess_bulk   ess_tail      rhat   e     Symbol   Float64   Float64   Float64    Float64    Float64   Float64     ⋯

           b    0.1108    0.0097    0.0008   148.2068   291.8095    1.0127     ⋯
           ā    0.0000    0.0001    0.0000   236.9165   337.4538    1.0070     ⋯
        a[1]    0.0000    0.0000    0.0000   155.5239   298.8641    1.0131     ⋯
        a[2]    0.0000    0.0000    0.0000   151.6236   330.0986    1.0126     ⋯
        a[3]    0.0000    0.0000    0.0000   148.6310   279.3477    1.0138     ⋯
           c    0.0011    0.0004    0.0000   285.6626   376.7046    1.0054     ⋯
           k    2.1275    1.1868    0.0491   338.1275   247.8097    1.0043     ⋯
                                                                1 column omitted

Quantiles
  parameters      2.5%     25.0%     50.0%     75.0%     97.5% 
      Symbol   Float64   Float64   Float64   Float64   Float64 

           b    0.0932    0.1040    0.1098    0.1170    0.1316
           ā    0.0000    0.0000    0.0000    0.0000    0.0002
        a[1]    0.0000    0.0000    0.0000    0.0000    0.0000
        a[2]    0.0000    0.0000    0.0000    0.0000    0.0000
        a[3]    0.0000    0.0000    0.0000    0.0000    0.0001
           c    0.0005    0.0008    0.0011    0.0013    0.0019
           k    1.0188    1.3479    1.7929    2.4963    5.3336
risk_factors4 = [mean(chain4[Symbol("a[$f]")]) for f in 1:3]

risk_factors4 ./ risk_factors4[2]

let data = data2

    data_weight = sqrt.(data.exposures) / 2
    color_i = data.risk_level

    p, ax, _ = scatter(
        data.att_age,
        data.fraction,
        markersize=data_weight,
        alpha=0.5,
        color=color_i,
        label="Experience data point (size indicates relative exposure quantity)",
        axis=(xlabel="age",
            limits=(nothing, nothing, -0.01, 0.10),
            ylabel="mortality rate",
            title="Parametric Bayesian Mortality"
        )
    )


    # show n samples from the posterior plotted on the graph
    n = 100

    ages = sort!(unique(data.att_age))
    for r in 1:3
        for i in 1:n
            s = sample(chain4, 1)
            a = only(s[Symbol("a[$r]")])
            b = only(s[:b])
            k = only(s[:k])
            c = 0
            m = MortalityTables.MakehamBeard(; a, b, c, k)
            lines!(ages, age -> MortalityTables.hazard(m, age), label="risk level $r", alpha=0.2, color=(CairoMakie.Makie.wong_colors()[r], 0.2))
        end
    end
    axislegend(ax, merge=true)
    p
end

31.7 Model Predictions

We can generate predictive estimates by passing a vector of missing in place of the outcome variables and then calling predict.

We get a table of values where each row is the the prediction implied by the corresponding chain sample, and the columns are the predicted value for each of the outcomes in our original dataset.

preds = predict(mortality4(data2, fill(missing, length(data2.deaths))), chain4)
Chains MCMC chain (1000×615×1 Array{Float64, 3}):

Iterations        = 1:1:1000
Number of chains  = 1
Samples per chain = 1000
parameters        = deaths[1], deaths[2], deaths[3], deaths[4], deaths[5], deaths[6], deaths[7], deaths[8], deaths[9], deaths[10], deaths[11], deaths[12], deaths[13], deaths[14], deaths[15], deaths[16], deaths[17], deaths[18], deaths[19], deaths[20], deaths[21], deaths[22], deaths[23], deaths[24], deaths[25], deaths[26], deaths[27], deaths[28], deaths[29], deaths[30], deaths[31], deaths[32], deaths[33], deaths[34], deaths[35], deaths[36], deaths[37], deaths[38], deaths[39], deaths[40], deaths[41], deaths[42], deaths[43], deaths[44], deaths[45], deaths[46], deaths[47], deaths[48], deaths[49], deaths[50], deaths[51], deaths[52], deaths[53], deaths[54], deaths[55], deaths[56], deaths[57], deaths[58], deaths[59], deaths[60], deaths[61], deaths[62], deaths[63], deaths[64], deaths[65], deaths[66], deaths[67], deaths[68], deaths[69], deaths[70], deaths[71], deaths[72], deaths[73], deaths[74], deaths[75], deaths[76], deaths[77], deaths[78], deaths[79], deaths[80], deaths[81], deaths[82], deaths[83], deaths[84], deaths[85], deaths[86], deaths[87], deaths[88], deaths[89], deaths[90], deaths[91], deaths[92], deaths[93], deaths[94], deaths[95], deaths[96], deaths[97], deaths[98], deaths[99], deaths[100], deaths[101], deaths[102], deaths[103], deaths[104], deaths[105], deaths[106], deaths[107], deaths[108], deaths[109], deaths[110], deaths[111], deaths[112], deaths[113], deaths[114], deaths[115], deaths[116], deaths[117], deaths[118], deaths[119], deaths[120], deaths[121], deaths[122], deaths[123], deaths[124], deaths[125], deaths[126], deaths[127], deaths[128], deaths[129], deaths[130], deaths[131], deaths[132], deaths[133], deaths[134], deaths[135], deaths[136], deaths[137], deaths[138], deaths[139], deaths[140], deaths[141], deaths[142], deaths[143], deaths[144], deaths[145], deaths[146], deaths[147], deaths[148], deaths[149], deaths[150], deaths[151], deaths[152], deaths[153], deaths[154], deaths[155], deaths[156], deaths[157], deaths[158], deaths[159], deaths[160], deaths[161], deaths[162], deaths[163], deaths[164], deaths[165], deaths[166], deaths[167], deaths[168], deaths[169], deaths[170], deaths[171], deaths[172], deaths[173], deaths[174], deaths[175], deaths[176], deaths[177], deaths[178], deaths[179], deaths[180], deaths[181], deaths[182], deaths[183], deaths[184], deaths[185], deaths[186], deaths[187], deaths[188], deaths[189], deaths[190], deaths[191], deaths[192], deaths[193], deaths[194], deaths[195], deaths[196], deaths[197], deaths[198], deaths[199], deaths[200], deaths[201], deaths[202], deaths[203], deaths[204], deaths[205], deaths[206], deaths[207], deaths[208], deaths[209], deaths[210], deaths[211], deaths[212], deaths[213], deaths[214], deaths[215], deaths[216], deaths[217], deaths[218], deaths[219], deaths[220], deaths[221], deaths[222], deaths[223], deaths[224], deaths[225], deaths[226], deaths[227], deaths[228], deaths[229], deaths[230], deaths[231], deaths[232], deaths[233], deaths[234], deaths[235], deaths[236], deaths[237], deaths[238], deaths[239], deaths[240], deaths[241], deaths[242], deaths[243], deaths[244], deaths[245], deaths[246], deaths[247], deaths[248], deaths[249], deaths[250], deaths[251], deaths[252], deaths[253], deaths[254], deaths[255], deaths[256], deaths[257], deaths[258], deaths[259], deaths[260], deaths[261], deaths[262], deaths[263], deaths[264], deaths[265], deaths[266], deaths[267], deaths[268], deaths[269], deaths[270], deaths[271], deaths[272], deaths[273], deaths[274], deaths[275], deaths[276], deaths[277], deaths[278], deaths[279], deaths[280], deaths[281], deaths[282], deaths[283], deaths[284], deaths[285], deaths[286], deaths[287], deaths[288], deaths[289], deaths[290], deaths[291], deaths[292], deaths[293], deaths[294], deaths[295], deaths[296], deaths[297], deaths[298], deaths[299], deaths[300], deaths[301], deaths[302], deaths[303], deaths[304], deaths[305], deaths[306], deaths[307], deaths[308], deaths[309], deaths[310], deaths[311], deaths[312], deaths[313], deaths[314], deaths[315], deaths[316], deaths[317], deaths[318], deaths[319], deaths[320], deaths[321], deaths[322], deaths[323], deaths[324], deaths[325], deaths[326], deaths[327], deaths[328], deaths[329], deaths[330], deaths[331], deaths[332], deaths[333], deaths[334], deaths[335], deaths[336], deaths[337], deaths[338], deaths[339], deaths[340], deaths[341], deaths[342], deaths[343], deaths[344], deaths[345], deaths[346], deaths[347], deaths[348], deaths[349], deaths[350], deaths[351], deaths[352], deaths[353], deaths[354], deaths[355], deaths[356], deaths[357], deaths[358], deaths[359], deaths[360], deaths[361], deaths[362], deaths[363], deaths[364], deaths[365], deaths[366], deaths[367], deaths[368], deaths[369], deaths[370], deaths[371], deaths[372], deaths[373], deaths[374], deaths[375], deaths[376], deaths[377], deaths[378], deaths[379], deaths[380], deaths[381], deaths[382], deaths[383], deaths[384], deaths[385], deaths[386], deaths[387], deaths[388], deaths[389], deaths[390], deaths[391], deaths[392], deaths[393], deaths[394], deaths[395], deaths[396], deaths[397], deaths[398], deaths[399], deaths[400], deaths[401], deaths[402], deaths[403], deaths[404], deaths[405], deaths[406], deaths[407], deaths[408], deaths[409], deaths[410], deaths[411], deaths[412], deaths[413], deaths[414], deaths[415], deaths[416], deaths[417], deaths[418], deaths[419], deaths[420], deaths[421], deaths[422], deaths[423], deaths[424], deaths[425], deaths[426], deaths[427], deaths[428], deaths[429], deaths[430], deaths[431], deaths[432], deaths[433], deaths[434], deaths[435], deaths[436], deaths[437], deaths[438], deaths[439], deaths[440], deaths[441], deaths[442], deaths[443], deaths[444], deaths[445], deaths[446], deaths[447], deaths[448], deaths[449], deaths[450], deaths[451], deaths[452], deaths[453], deaths[454], deaths[455], deaths[456], deaths[457], deaths[458], deaths[459], deaths[460], deaths[461], deaths[462], deaths[463], deaths[464], deaths[465], deaths[466], deaths[467], deaths[468], deaths[469], deaths[470], deaths[471], deaths[472], deaths[473], deaths[474], deaths[475], deaths[476], deaths[477], deaths[478], deaths[479], deaths[480], deaths[481], deaths[482], deaths[483], deaths[484], deaths[485], deaths[486], deaths[487], deaths[488], deaths[489], deaths[490], deaths[491], deaths[492], deaths[493], deaths[494], deaths[495], deaths[496], deaths[497], deaths[498], deaths[499], deaths[500], deaths[501], deaths[502], deaths[503], deaths[504], deaths[505], deaths[506], deaths[507], deaths[508], deaths[509], deaths[510], deaths[511], deaths[512], deaths[513], deaths[514], deaths[515], deaths[516], deaths[517], deaths[518], deaths[519], deaths[520], deaths[521], deaths[522], deaths[523], deaths[524], deaths[525], deaths[526], deaths[527], deaths[528], deaths[529], deaths[530], deaths[531], deaths[532], deaths[533], deaths[534], deaths[535], deaths[536], deaths[537], deaths[538], deaths[539], deaths[540], deaths[541], deaths[542], deaths[543], deaths[544], deaths[545], deaths[546], deaths[547], deaths[548], deaths[549], deaths[550], deaths[551], deaths[552], deaths[553], deaths[554], deaths[555], deaths[556], deaths[557], deaths[558], deaths[559], deaths[560], deaths[561], deaths[562], deaths[563], deaths[564], deaths[565], deaths[566], deaths[567], deaths[568], deaths[569], deaths[570], deaths[571], deaths[572], deaths[573], deaths[574], deaths[575], deaths[576], deaths[577], deaths[578], deaths[579], deaths[580], deaths[581], deaths[582], deaths[583], deaths[584], deaths[585], deaths[586], deaths[587], deaths[588], deaths[589], deaths[590], deaths[591], deaths[592], deaths[593], deaths[594], deaths[595], deaths[596], deaths[597], deaths[598], deaths[599], deaths[600], deaths[601], deaths[602], deaths[603], deaths[604], deaths[605], deaths[606], deaths[607], deaths[608], deaths[609], deaths[610], deaths[611], deaths[612], deaths[613], deaths[614], deaths[615]
internals         = 

Summary Statistics
  parameters      mean       std      mcse    ess_bulk    ess_tail      rhat      Symbol   Float64   Float64   Float64     Float64     Float64   Float64   ⋯

   deaths[1]    0.1090    0.3244    0.0107    913.3787    909.9676    0.9992   ⋯
   deaths[2]    0.1110    0.3268    0.0104    990.4136    987.3694    0.9998   ⋯
   deaths[3]    0.1100    0.3286    0.0104   1001.5529   1010.5515    0.9990   ⋯
   deaths[4]    0.1210    0.3442    0.0116    881.3675    758.0117    1.0029   ⋯
   deaths[5]    0.0950    0.3132    0.0097   1029.7060   1014.3181    0.9992   ⋯
   deaths[6]    0.0950    0.3132    0.0104    904.7898    913.0043    0.9994   ⋯
   deaths[7]    0.1110    0.3329    0.0109    939.9057    960.0101    0.9997   ⋯
   deaths[8]    0.1220    0.3482    0.0120    837.3789    822.8354    0.9994   ⋯
   deaths[9]    0.1110    0.3175    0.0103    948.2237    941.1233    0.9990   ⋯
  deaths[10]    0.1240    0.3532    0.0111   1009.1972   1011.2902    0.9990   ⋯
  deaths[11]    0.0960    0.3145    0.0102    947.1295    953.3921    1.0023   ⋯
  deaths[12]    0.1040    0.3183    0.0099    996.1686    917.0357    1.0003   ⋯
  deaths[13]    0.1130    0.3498    0.0112    958.1668    943.4942    1.0008   ⋯
  deaths[14]    0.1170    0.3368    0.0112    922.3999    985.5505    1.0004   ⋯
  deaths[15]    0.1100    0.3194    0.0102    980.4853    971.7818    0.9994   ⋯
  deaths[16]    0.1040    0.3183    0.0092   1190.2268   1010.1399    0.9993   ⋯
  deaths[17]    0.1570    0.3981    0.0134    877.2075    869.1933    1.0018   ⋯
      ⋮           ⋮         ⋮         ⋮          ⋮           ⋮          ⋮      ⋱
                                                   1 column and 598 rows omitted

Quantiles
  parameters      2.5%     25.0%     50.0%     75.0%     97.5% 
      Symbol   Float64   Float64   Float64   Float64   Float64 

   deaths[1]    0.0000    0.0000    0.0000    0.0000    1.0000
   deaths[2]    0.0000    0.0000    0.0000    0.0000    1.0000
   deaths[3]    0.0000    0.0000    0.0000    0.0000    1.0000
   deaths[4]    0.0000    0.0000    0.0000    0.0000    1.0000
   deaths[5]    0.0000    0.0000    0.0000    0.0000    1.0000
   deaths[6]    0.0000    0.0000    0.0000    0.0000    1.0000
   deaths[7]    0.0000    0.0000    0.0000    0.0000    1.0000
   deaths[8]    0.0000    0.0000    0.0000    0.0000    1.0000
   deaths[9]    0.0000    0.0000    0.0000    0.0000    1.0000
  deaths[10]    0.0000    0.0000    0.0000    0.0000    1.0000
  deaths[11]    0.0000    0.0000    0.0000    0.0000    1.0000
  deaths[12]    0.0000    0.0000    0.0000    0.0000    1.0000
  deaths[13]    0.0000    0.0000    0.0000    0.0000    1.0000
  deaths[14]    0.0000    0.0000    0.0000    0.0000    1.0000
  deaths[15]    0.0000    0.0000    0.0000    0.0000    1.0000
  deaths[16]    0.0000    0.0000    0.0000    0.0000    1.0000
  deaths[17]    0.0000    0.0000    0.0000    0.0000    1.0000
      ⋮           ⋮         ⋮         ⋮         ⋮         ⋮
                                                598 rows omitted