Skip to content

Summaries and diagnostics ​

Turning a fitted chain into the tables the report prints, and measuring how the sampler behaved. The diagnostics cover R-hat, effective sample size and divergences, both overall and per parameter.

Index ​

Reference ​

BVDOutbreakSize.MarkdownTable Type
julia
struct MarkdownTable

A table for a Literate page to render as a table rather than as a block of printed output. Display it as the last expression of a chunk.

Literate picks a chunk's output format from what the value is showable as, taking text/html before text/markdown. A bare DataFrame is html-showable, so it goes out as a @raw html block, and Documenter compiles a regex from each raw block's own text, which fails once the block passes PCRE's ~64KB compiled-pattern limit. Several of the scoring tables cross it. This wrapper is showable as markdown and not as html, so the table goes out as an ordinary markdown table.

A DataFrame is rendered by markdown_table. Anything else is taken through its own markdown rendering, or its plain-text one where it has none, so a page can pass either a table or a placeholder message.


Fields

  • text::String
source
BVDOutbreakSize.bias_sample Method
julia
bias_sample(
    observed::Real,
    predicted::AbstractVector{<:Real}
) -> Any

Sample-based forecast bias for a single observation against a predictive sample, following scoringutils::bias_sample. With n_lt and n_eq the counts of predictive draws strictly below and exactly equal to the observation, the bias is

   

over the m draws. It lies in  . Negative means the predictive distribution sits below the observation (under-prediction), positive that it sits above, and zero that the observation falls at the predictive median. The equal-count term handles ties in count data, so a mass of draws exactly at the observation does not bias the score.

source
BVDOutbreakSize.comparison_table Method
julia
comparison_table(
    C_draws::AbstractVector;
    scenarios
) -> DataFrames.DataFrame

For each published C_T scenario, the narrowest joint posterior credible interval (30, 60 or 90%) that contains it, or "outside 90%".

source
BVDOutbreakSize.diagnostics_table Method
julia
diagnostics_table(fits::Pair{String}...) -> Any

DataFrame of fit-quality diagnostics with one row per fit. Pass each fit as "label" => chain. Columns :fit, :max_rhat, :min_ess_bulk, :divergences.

source
BVDOutbreakSize.fit_diagnostics Method
julia
fit_diagnostics(
    chn
) -> NamedTuple{(:max_rhat, :min_ess_bulk, :min_ess_tail, :n_divergent, :n_draws), <:Tuple{Float64, Float64, Float64, Any, Any}}

NUTS fit-quality summary for one chain: the worst (maximum) R-hat, the smallest bulk and tail effective sample sizes across parameters, the number of divergent transitions and the number of post-warmup draws they are out of.

source
BVDOutbreakSize.markdown_table Method
julia
markdown_table(
    df::DataFrames.DataFrame
) -> Union{Base.AnnotatedString{String}, String}

GitHub-flavoured markdown table for a DataFrame, one header row from the column names and one body row per data row. Used to persist a rendered table to disk so a static page can embed it without re-running the fit, and by MarkdownTable to put a table on a Literate page.

source
BVDOutbreakSize.median_interval_text Method
julia
median_interval_text(draws; digits, scale, suffix) -> String
julia
median_interval_text(draws; digits = 0, scale = 1, suffix = "")
    -> String

A posterior as a phrase for a summary bullet: the median and the equal-tailed 90% credible interval, "about m (90% credible interval lo to hi)". Counts round to whole numbers by default. Otherwise each value is shown to digits decimals, so 0.8 reads 0.80. Each value is multiplied by scale and followed by suffix, so scale = 100, suffix = "%" reads a share as a percentage.

source
BVDOutbreakSize.onsets_over_time Method
julia
onsets_over_time(chn; n, seeding)

Per-date posterior summary of the latent symptom-onset trajectory, one row per grid day from seeding (grid day 1) to the cut-off (grid day n). Each row gives the equal-tailed 30%, 60% and 90% credible intervals of both the daily new symptom onsets and the cumulative symptom onsets to that date. The chain must carry the vector deterministic cumulative_onsets, one trajectory per draw, as the joint fit does.

Columns: date, then for each of new_onsets and cumulative_onsets the six endpoints _lower_90, _lower_60, _lower_30, _upper_30, _upper_60, _upper_90.

source
BVDOutbreakSize.patch_headline Function
julia
patch_headline(
    chn;
    ...
) -> Union{Base.AnnotatedString{String}, String}
patch_headline(
    chn,
    n_patches::Integer;
    patch_labels
) -> Union{Base.AnnotatedString{String}, String}

Markdown table comparing the provinces of the patch model, one row per province and each quantity as an equal-tailed 90% credible interval, followed by the comparisons across provinces as sentences. Every comparison is computed draw by draw, so it carries the correlation between provinces.

The columns are the share of infections to date, the reproduction number at the cut-off, the posterior probability that it is above one, and, when the chain carries them, the case-fatality ratio (CFR_patch) and the case ascertainment relative to the national average (province_ascertainment).

The sentences give the probability that the largest province has the most infections, how many provinces are more likely than not to be growing, and, when the chain carries importation_patch, the share of infections to date imported from another province. patch_summary_table gives each province's own intervals.

source
BVDOutbreakSize.patch_overview_table Function
julia
patch_overview_table(chn; ...) -> DataFrames.DataFrame
patch_overview_table(
    chn,
    n_patches::Integer;
    digits,
    patch_labels
) -> DataFrames.DataFrame

Cross-province overview for the patch model: one row per province and one column per quantity, each a median with a 90% credible interval. Reads the reproduction number at the cut-off, cumulative infections, the province's share of national infections, and its case ascertainment relative to the national average. The per-province detail, with the full 30/60/90% intervals and the deviation parameters, is in patch_summary_table.

The infection share is computed per draw before summarising, so its interval carries the correlation between provinces rather than dividing two independently summarised numbers. Ascertainment and the reproduction number must be read together. The case composition identifies only their product, and it is the per-province deaths that tilt the balance between them.

source
BVDOutbreakSize.patch_summary_table Function
julia
patch_summary_table(chn; ...) -> DataFrames.DataFrame
patch_summary_table(
    chn,
    n_patches::Integer;
    digits,
    patch,
    patch_labels
) -> DataFrames.DataFrame

Per-patch outbreak summary for the patch model: one row per province, with the cut-off cumulative infections C_T, the cut-off reproduction number R_T, the daily infections at the cut-off, and the log-Rt deviation δ from the common national trend. Each is reported as the same 90/60/30% credible intervals summary_table uses.

Pass patch to restrict the table to a single province, by index or by label. The Patch column is then dropped, since it would repeat one value.

The deviations are sum-to-zero contrasts around the national trend (see patch_rt_model), so δ is read relative to the national average across provinces, not relative to any one patch: a negative δ means that province transmits below the national trend. Every patch, including the primary, carries its own deviation, and they sum to zero in every draw. For the log-Rt of a province relative to Ituri specifically, read the chain's log_rt_contrast instead.

Expects a chain from bvd_joint, which stores the per-patch quantities as vector deterministics (C_T_patch, R_T_patch, infections_T_patch, delta_patch), one entry per patch.

source
BVDOutbreakSize.posterior_summary Method
julia
posterior_summary(
    xs
) -> NamedTuple{(:lo90, :lo60, :lo30, :hi30, :hi60, :hi90), <:NTuple{6, Any}}

Return (lo90, lo60, lo30, hi30, hi60, hi90) equal-tailed credible interval endpoints from a vector of draws.

source
BVDOutbreakSize.province_bed_table Function
julia
province_bed_table(chn; ...) -> DataFrames.DataFrame
province_bed_table(
    chn,
    n_patches::Integer;
    digits,
    patch_labels
) -> DataFrames.DataFrame

Per-province isolation beds at the cut-off, from the province occupancy and bed splits in treatment_flow_model: one row per province with the beds, the latent bed demand, the occupied beds (the national occupancy split on the demand shares and capped at the beds), the utilisation and the demand above the beds, each a median with a 90% credible interval. Expects a chain from bvd_joint with more than one patch, which stores these as the vector deterministics province_bed_capacity, province_bed_demand, province_expected_isolation, province_bed_utilisation and province_bed_shortfall.

source
BVDOutbreakSize.province_cfr_table Method
julia
province_cfr_table(
    chn,
    res;
    province_cases,
    province_deaths,
    n_patches,
    patch_labels,
    digits
)

Per-province case-fatality ratios, set against the national ones the confirmed_cfr_table reports. res is a delay_corrected_confirmed_cfr result and chn the patch chain.

Three quantities per province, each a median with a 90% credible interval except the observed ratio, which is a count:

  • the naive observed confirmed ratio, that province's reported confirmed deaths over its reported confirmed cases;

  • the delay-corrected confirmed ratio, the national corrected ratio scaled by the province's relative lethality and death confirmation over its relative case ascertainment, which is what varies by province once the delays are corrected for;

  • the structural, infection-based ratio, read from CFR_patch, the national ratio times that province's sum-to-zero lethality contrast.

The death composition identifies only the product of the lethality and the death-confirmation contrasts, so their split is set by their priors. The lethality prior is the looser of the two, so a provincial excess of deaths over cases is read first as lethality. A chain carrying no CFR_patch falls back to the national ratio in the structural column.

province_cases and province_deaths are the observed per-province confirmed case and death totals over the fitted window, in the order of patch_labels.

source
BVDOutbreakSize.province_composition_panels Method
julia
province_composition_panels(
    chn;
    share_key,
    obs_increments,
    stream,
    n_patches,
    patch_labels,
    rho_key
)

Per-province posterior-predictive panels for one province composition, in the shape stream_calibration takes. One panel per province, titled "<stream>, <province>", holding the observed count at each spatial vintage and one replicate count vector per posterior draw.

The replicates push each draw's expected shares (share_key) and the composition's overdispersion back through the stick-breaking allocation at the vintage's observed total, the same predictive plot_province_composition_ppc draws as its grey band. The check is therefore on the split alone, conditional on the national total. A vintage with no observed cases has no split to predict and is dropped. Each panel is a daily panel (cumulative = false), since a vintage's count is its own increment.

rho_key names the overdispersion and defaults to the one matching share_key. Errors when the chain carries none, since without it there is no predictive to score.

source
BVDOutbreakSize.province_count_panels Method
julia
province_count_panels(
    chn;
    share_key,
    obs_increments,
    province_days,
    national_days,
    national_replicates,
    stream,
    baseline,
    n_patches,
    patch_labels,
    rho_key
)

Per-province posterior-predictive panels for one province stream on the count scale, in the shape stream_calibration takes. One panel per province, titled "<stream>, <province>", holding the observed count at each spatial vintage and one replicate count vector per posterior draw.

Unlike province_composition_panels, the total being split is not held at its observed value. Each draw's national replicate increments (national_replicates, one vector per draw on the national_days grid, from predict) are summed over each province vintage, and that draw's expected shares (share_key) and overdispersion split the sum through the same stick-breaking allocation. The replicates therefore carry the national observation model's uncertainty as well as the split's.

The first province vintage is the cumulative count to date, so it takes baseline, the national count before the first replicated increment, as well as every national increment up to it. Every province day must be on the national grid, and the national replicates must pair one to one with the chain draws, each with one increment per national day. Each panel is a daily panel (cumulative = false).

source
BVDOutbreakSize.province_forecast_headline Method
julia
province_forecast_headline(
    fc::DataFrames.DataFrame;
    n_patches,
    patch_labels
) -> Union{Base.AnnotatedString{String}, String}

Markdown table comparing the provinces' one-week-ahead forecast, one row per province and each target as an equal-tailed 90% predictive interval, followed by bullets that compare the provinces. Every comparison is computed draw by draw from the forecast_provinces frame fc, so it carries the correlation between provinces, and the provinces add up to the national forecast.

The columns are the new confirmed cases and confirmed deaths over the week, the patients in isolation and the isolation beds at the end of the week, the new admissions and new infections over the week, the reproduction number at the end of the week and the probability that it is above one, each when fc carries it.

source
BVDOutbreakSize.province_forecast_table Method
julia
province_forecast_table(
    pp,
    fc;
    n_patches,
    patch_labels,
    horizon,
    digits
) -> DataFrames.DataFrame

One-week-ahead forecast by province: the new confirmed cases and confirmed deaths expected in each province over the week to T + 7, as the same 90/60/30% credible intervals forecast_table reports nationally. The same content is drawn by plot_province_forecast and archived for scoring by province_forecast_archive.

fc is a forecast_provinces frame. A national forecast_reported result is replaced by the province forecast read from the posterior-predictive draws pp at horizon days, which also labels the rows. The table adds each province's patients in isolation and isolation beds, its new latent infections and its reproduction number at the horizon.

source
BVDOutbreakSize.province_forecast_vs_truth Method
julia
province_forecast_vs_truth(
    pp,
    fc;
    observed,
    baseline,
    death_observed,
    death_baseline,
    n_patches,
    patch_labels,
    horizon,
    digits
)

Per-province forecast against what was observed. pp is the posterior-predictive draws of a frozen patch fit (forecast_draws), and observed and baseline the per-province cumulative counts at the target date and at the forecast origin, so the truth is their difference.

Each province's forecast is the forecast_provinces forecast from pp over horizon days. fc is either that frame or a national forecast_reported result, which is replaced by the province forecast from the same draws.

Reports the 90% predictive interval, the observed count, and whether the observation fell inside the interval, one row per province and stream. No central estimate is reported.

source
BVDOutbreakSize.province_recent_counts Method
julia
province_recent_counts(
    province_history,
    province_names::AbstractVector,
    n_patches::Integer;
    window
) -> Union{Nothing, NamedTuple{(:start_day, :last_day, :counts), <:Tuple{Any, Any, Any}}}
julia
province_recent_counts(province_history, province_names, n_patches;
                       window = 7)

New counts in each patch over the most recent window days of the per-province cumulative histories, read off the same increments province_increment_matrix builds, so a downward revision counts as no new cases.

The span runs from the latest vintage at least window days before the last one to the last vintage, so it can be longer than window when the tables skip days. Returns (; start_day, last_day, counts) with the span's grid day indices and one count per patch, or nothing when there is no history or it is shorter than the window.

source
BVDOutbreakSize.province_share_draws Method
julia
province_share_draws(fc, col::Symbol; n_patches) -> Vector
julia
province_share_draws(fc, col; n_patches) -> Vector{Vector{Float64}}

Each province's share of the provinces' combined projection of col, draw by draw, from a forecast_provinces frame. Returns one vector of share draws per patch. A draw in which no province projects anything has no share and is left out.

source
BVDOutbreakSize.stream_calibration Method
julia
stream_calibration(
    panels::AbstractVector
) -> DataFrames.DataFrame

Per-stream posterior-predictive calibration for the per-vintage one-step-ahead conditional checks. Pass the same panels given to plot_vintage_conditional_ppc: each is a NamedTuple (; title, observed, replicates, …) with optional cumulative. For each stream the conditional predictive at every vintage is scored against the observed count, and the per-vintage scores are averaged into one row.

Columns: stream, the number of scored vintages n, the mean forecast bias from bias_sample, and the empirical coverage_50/coverage_90, the fraction of vintages whose observed count falls inside the central 50% and 90% predictive intervals. A well-calibrated stream has bias near zero and coverage near its nominal level.

source
BVDOutbreakSize.streams_table Method
julia
streams_table(
    streams::Pair{String, <:AbstractVector}...;
    digits
) -> DataFrames.DataFrame

Side-by-side credible intervals for C_T from several fits. Pass each fit as "label" => draws_vector.

source
BVDOutbreakSize.summary_table Method
julia
summary_table(
    chn,
    params::AbstractVector{Symbol};
    digits,
    labels
) -> DataFrames.DataFrame

DataFrame with one row per posterior parameter and the columns Quantity, Lower 90%, Lower 60%, Lower 30%, Upper 30%, Upper 60%, Upper 90% giving the endpoints of the equal-tailed 30%, 60% and 90% credible intervals.

doubling_time is reported as the image of r's interval rather than from its own draws, because it is unbounded at zero growth. Its row runs from the fastest decline through the zero-growth pole to the fastest growth, and is not sorted by value.

labels maps a raw chain symbol to a display name (e.g. Symbol("rt_state.sigma_rw") => "Rt step size"), applied to the Quantity column only. Symbols absent from the map keep their raw name.

source
BVDOutbreakSize.diagnostic_contrast Method
julia
diagnostic_contrast(
    reference::Pair{String},
    fits::Pair{String}...
) -> DataFrames.DataFrame

Bulk effective sample size for every parameter element shared between a reference fit and each of the fits it is compared with. Pass the reference as "label" => chain first, then each comparison fit the same way. A frame parameter_diagnostics has already produced stands in for a chain on either side.

ess_ratio is the reference fit's bulk effective sample size over the comparison fit's. A ratio well below one is a parameter that mixes in the comparison fit and stops mixing in the reference, which points at what the reference adds rather than at the parameter itself.

Columns are :fit, :parameter, :index, :rhat, :ess_bulk, :rhat_reference, :ess_bulk_reference and :ess_ratio.

source
BVDOutbreakSize.diagnostic_contrast_table Method
julia
diagnostic_contrast_table(
    df::DataFrames.DataFrame;
    n,
    collapse_aliases,
    labels
) -> DataFrames.DataFrame

The n parameter elements whose mixing degrades most from a comparison fit to the reference fit, taken from diagnostic_contrast and ranked by the ratio of the two bulk effective sample sizes from the smallest up. collapse_aliases drops a parameter whose numbers repeat those of one already listed for the same fit.

Columns are :fit, :parameter, :ess_bulk, :ess_bulk_reference and :ess_ratio.

source
BVDOutbreakSize.diagnostic_spread_table Method
julia
diagnostic_spread_table(
    fits::Pair{String}...;
    rhat_warn,
    rhat_bad,
    ess_low,
    labels
) -> DataFrames.DataFrame

How far R-hat and the effective sample size spread across the parameters of each fit, one row per fit. Pass each fit as "label" => chain, or as "label" => frame where the frame is one parameter_diagnostics has already produced.

A fit can carry a bad worst-case R-hat because one parameter is stuck, or because most of the model is. The counts here separate those two cases. The last column names the parameter whose elements reach the lowest bulk effective sample size.

source
BVDOutbreakSize.divergence_location_table Method
julia
divergence_location_table(
    chn;
    n,
    width,
    collapse_aliases,
    exclude,
    labels
) -> Any

Where the divergent transitions of chn sit in parameter space, one row per scalar parameter, ranked by separation from the largest down.

separation is how far the divergent draws sit from the posterior as a whole, in standard deviations of the full posterior, signed by direction. A separation near zero means divergences scattered across the posterior; a large one means they concentrate in one region of that parameter.

The two interval columns give the middle width of all draws and of the divergent draws alone. Vector-valued parameters are skipped, as are parameters that never move, and collapse_aliases drops a parameter whose intervals and separation repeat those of one already listed.

source
BVDOutbreakSize.family_diagnostics_table Method
julia
family_diagnostics_table(
    fit;
    n,
    rhat_threshold,
    collapse_aliases,
    labels
) -> Any

Fit diagnostics grouped by parameter, one row per parameter name rather than per element, ranked by bulk effective sample size from the lowest up. A vector-valued parameter collapses to a single row, so a group whose elements all mix badly is visible as one line rather than as hundreds.

rhat_threshold sets the R-hat an element has to exceed to be counted in the last column, and collapse_aliases drops a parameter whose diagnostics repeat those of one already listed. Columns are :parameter, :elements, :max_rhat, :min_ess_bulk and the count above the threshold.

source
BVDOutbreakSize.parameter_diagnostics Method
julia
parameter_diagnostics(chn; exclude) -> Any

Per-parameter fit diagnostics for one chain, one row per scalar parameter element. A vector-valued parameter contributes one row per element, numbered in index from one, and parameter carries the name without the index; a scalar parameter carries index zero. Trajectories named in exclude are skipped, as they are in the headline fit_diagnostics summary, and so is any quantity that is not finite in some draw.

Columns are :parameter, :index, :rhat, :ess_bulk and :ess_tail. Rows whose R-hat is undefined are dropped, so a fixed or degenerate quantity cannot rank ahead of a parameter that genuinely mixes badly.

source
BVDOutbreakSize.sampler_by_chain_table Method
julia
sampler_by_chain_table(chn) -> DataFrames.DataFrame

Sampler behaviour per chain: how many draws each chain took, how many of them were divergent, the step size it adapted to and the deepest tree it built.

One chain with a far smaller step size and far deeper trees than the others is a chain stuck somewhere the rest of the posterior never visits, which is a different problem from divergences spread evenly across chains.

source
BVDOutbreakSize.worst_parameters_table Method
julia
worst_parameters_table(
    fit;
    n,
    collapse_aliases,
    labels
) -> DataFrames.DataFrame

The n worst-mixing parameter elements of a fit, ranked by bulk effective sample size from the lowest up. fit is a chain or a frame parameter_diagnostics has already produced. labels maps a parameter name to the display name the report uses for it. With collapse_aliases a parameter whose diagnostics repeat those of one already listed is dropped, since it is the same quantity under a second name.

Columns are :parameter, :rhat, :ess_bulk and :ess_tail.

source