Health-zone estimates
This page splits each patch of the joint fit across its health zones, giving each zone's reproduction number, share of the patch, one-week forecast and probability of at least a few cases. The health-zone model on the methods page gives the maths, and the interactive map is on the dashboard. The one-week zone forecast is on the health-zone forecasts page and its scores in the health-zone forecast evaluation.
Load packages, data and fitted chains
# Shared setup: packages, observations and the fit registry. See
# `docs/pages/_setup.jl`.
using BVDOutbreakSize
include(joinpath(pkgdir(BVDOutbreakSize), "docs", "pages", "_setup.jl"))validation_forecast_from (generic function with 1 method)# The fits this page reads, loaded from the cache here: the headline joint,
# the zone fit melded from it, and the frozen zone fit the sampler
# diagnostics read.
chn_joint = load_fit("joint");
chn_local = load_fit("local");
frozen_local = load_fit("local_frozen_validation");
# The zone stage's fixed inputs with the joint's forecast, from the shared
# setup, so the health-zone forecast page draws the same forecast from the
# same inputs.
zone_inputs = zone_stage_inputs(; forecast = true);
zone_patch = zone_inputs.patch_of_zone;Last updated: 23 October 2026.
Data as of: 20 October 2026.
Estimates by zone
The interactive map on the dashboard adds the probability of at least
Health-zone post-processing
# The geojson keys a zone without the province prefix the manifest carries.
zone_map_keys = [
String(last(split(k, "."; limit = 2)))
for k in zone_inputs.zone_keys
];
# Daily zone reproduction numbers over the zone grid, each zone draw on
# its own draw of the patch trajectory so the patch uncertainty is
# carried, and the cut-off values the map and table read. A zone's
# reproduction number is reported from the day its cumulative infections
# reach the floor in the median draw.
zone_rt_traj = reconstruct_zone_rt(chn_local, zone_inputs);
zone_RT_finite = [filter(isfinite, m[:, obs.n]) for m in zone_rt_traj];
zone_RT_reported = findall(!isempty, zone_RT_finite);
zone_share_T = let vs = vec(collect(chn_local[:share_T_zone]))
[Float64[v[z] for v in vs] for z in eachindex(zone_map_keys)]
end;
_zq(v, p) = quantile(v, p)
# The one-week zone forecast drawn from the zone model, and the
# probability of at least K cases per zone from the same draws.
zone_fc = zone_forecast(chn_local, zone_inputs);
zone_fc_draws = zone_forecast_draws(zone_fc, zone_inputs);
ZONE_THRESHOLDS = (1, 5, 10, 20)
zone_fc_probs = zone_forecast_probabilities(
zone_fc, zone_inputs; thresholds = ZONE_THRESHOLDS, draws = zone_fc_draws
);
zone_overview = zone_overview_table(chn_local, zone_inputs);
# Cases allocated to each zone over the past one, two and four weeks,
# and the last vintage on which each zone's count rose.
zone_recent = Dict(
w => zone_recent_cases(zone_inputs; window = w) for w in (7, 14, 28)
);
zone_last_case = zone_last_case_dates(zone_inputs);
zone_map_fig = plot_zone_map_panels(
[
(;
values = [median(zone_RT_finite[z]) for z in zone_RT_reported],
zones = zone_map_keys[zone_RT_reported],
lower = [_zq(zone_RT_finite[z], 0.05) for z in zone_RT_reported],
upper = [_zq(zone_RT_finite[z], 0.95) for z in zone_RT_reported],
diverging_at = 1.0, scale = log10,
title = "Reproduction number at the cut-off",
colorbar_label = "R",
),
(;
values = [_zq(v, 0.05) for v in zone_fc_draws.zones],
zones = zone_map_keys, scale = CairoMakie.Makie.pseudolog10,
title = "Confirmed cases over the coming week (lower 90%)",
colorbar_label = "cases",
),
(;
values = [_zq(v, 0.95) for v in zone_fc_draws.zones],
zones = zone_map_keys, scale = CairoMakie.Makie.pseudolog10,
title = "Confirmed cases over the coming week (upper 90%)",
colorbar_label = "cases",
),
(;
values = Float64.(zone_inputs.cumulative), zones = zone_map_keys,
scale = CairoMakie.Makie.pseudolog10,
title = "Confirmed cases to date", colorbar_label = "cases",
),
];
ncols = 2, title = "Health zones at the cut-off"
);
Compute the weekly cases and reproduction number by zone
zone_cases_rt = cases_rt_table(
zone_rt_traj, zone_inputs.days, zone_inputs.counts;
cutoff = obs.cutoff, n = obs.n, areas = zone_inputs.zone_labels,
patch = zone_patch
);
zone_cases_rt_fig = plot_cases_rt(
zone_cases_rt; patch_labels = zone_inputs.patch_labels, top = 12,
unit = "health zone"
);
Zone overview table
# The overview as displayed: the interval strings, without the numeric
# columns.
zone_overview_display = let d = zone_overview[
:,
[:zone, :patch, :cases, :share, :R_T, :p_R_above_1, :delta_T],
]
d[!, "R modelled separately"] = [
w ? "yes" : "no" for w in zone_overview.walking
]
d
end;| zone | patch | cases | share | R_T | p_R_above_1 | delta_T | R modelled separately |
|---|---|---|---|---|---|---|---|
| Beni | Nord-Kivu | 553 | 48.0 (30.1–65.2) | 1.11 (0.75–1.55) | 0.67 | 0.11 (-0.39–0.66) | yes |
| Kalunguta | Nord-Kivu | 81 | 5.2 (2.8–9.9) | 1.12 (0.7–1.75) | 0.66 | -0.01 (-0.46–0.44) | yes |
| Oicha | Nord-Kivu | 54 | 5.8 (2.5–13.5) | 1.13 (0.65–1.91) | 0.66 | 0.09 (-0.52–0.72) | yes |
| Boma Mangbetu | Haut-Uele | 45 | 10.7 (5.5–18.7) | 1.12 (0.69–1.78) | 0.64 | -0.05 (-0.42–0.27) | yes |
| Mutwanga | Nord-Kivu | 31 | 2.5 (0.9–7.4) | 1.14 (0.62–1.94) | 0.63 | 0.11 (-0.44–0.68) | yes |
| Mabalako | Nord-Kivu | 83 | 8.1 (3.3–19.5) | 1.11 (0.59–1.97) | 0.61 | 0.14 (-0.43–0.71) | yes |
| Nyankunde | Ituri | 134 | 0.9 (0.3–2.8) | 1.09 (0.55–2.25) | 0.59 | -0.03 (-0.88–0.83) | yes |
| Lolwa | Ituri | 36 | 0.4 (0.1–1.6) | 1.08 (0.48–2.27) | 0.58 | -0.12 (-1.15–0.84) | yes |
| Wamba | Haut-Uele | 152 | 40.4 (21.8–63.4) | 1.07 (0.62–1.69) | 0.58 | 0.07 (-0.35–0.48) | yes |
| Tchomia | Ituri | 82 | 0.5 (0.1–2.2) | 1.08 (0.49–2.27) | 0.57 | 0.04 (-0.87–0.92) | yes |
| Kyondo | Nord-Kivu | 44 | 1.2 (0.5–3.0) | 1.07 (0.59–1.93) | 0.57 | -0.07 (-0.6–0.41) | yes |
| Mambasa | Ituri | 76 | 6.7 (1.8–24.3) | 1.08 (0.41–2.46) | 0.56 | 0.22 (-0.87–1.33) | yes |
| Kilo | Ituri | 38 | 0.2 (0.1–0.6) | 1.05 (0.52–2.0) | 0.55 | -0.08 (-0.88–0.62) | yes |
| Vuhovi | Nord-Kivu | 55 | 2.1 (1.0–4.8) | 1.04 (0.6–1.72) | 0.55 | -0.03 (-0.44–0.38) | yes |
| Butembo | Nord-Kivu | 319 | 6.0 (2.7–11.7) | 1.04 (0.58–1.7) | 0.54 | -0.09 (-0.53–0.3) | yes |
| Damas | Ituri | 41 | 0.2 (0.0–1.0) | 1.03 (0.44–2.41) | 0.52 | -0.05 (-1.05–0.9) | yes |
| Komanda | Ituri | 191 | 3.2 (1.0–10.0) | 1.01 (0.43–2.18) | 0.52 | -0.13 (-1.12–0.85) | yes |
| Rwampara | Ituri | 1173 | 8.4 (2.7–20.6) | 1.01 (0.41–1.92) | 0.51 | 0.07 (-0.74–0.85) | yes |
| Katwa | Nord-Kivu | 648 | 8.9 (4.2–17.2) | 1.01 (0.57–1.68) | 0.51 | -0.12 (-0.55–0.27) | yes |
| Isiro | Haut-Uele | 108 | 9.1 (3.5–25.3) | 1.02 (0.53–1.85) | 0.51 | -0.01 (-0.5–0.49) | yes |
All health zones
| zone | patch | cases | share | R_T | p_R_above_1 | delta_T | R modelled separately |
|---|---|---|---|---|---|---|---|
| Beni | Nord-Kivu | 553 | 48.0 (30.1–65.2) | 1.11 (0.75–1.55) | 0.67 | 0.11 (-0.39–0.66) | yes |
| Kalunguta | Nord-Kivu | 81 | 5.2 (2.8–9.9) | 1.12 (0.7–1.75) | 0.66 | -0.01 (-0.46–0.44) | yes |
| Oicha | Nord-Kivu | 54 | 5.8 (2.5–13.5) | 1.13 (0.65–1.91) | 0.66 | 0.09 (-0.52–0.72) | yes |
| Boma Mangbetu | Haut-Uele | 45 | 10.7 (5.5–18.7) | 1.12 (0.69–1.78) | 0.64 | -0.05 (-0.42–0.27) | yes |
| Mutwanga | Nord-Kivu | 31 | 2.5 (0.9–7.4) | 1.14 (0.62–1.94) | 0.63 | 0.11 (-0.44–0.68) | yes |
| Mabalako | Nord-Kivu | 83 | 8.1 (3.3–19.5) | 1.11 (0.59–1.97) | 0.61 | 0.14 (-0.43–0.71) | yes |
| Nyankunde | Ituri | 134 | 0.9 (0.3–2.8) | 1.09 (0.55–2.25) | 0.59 | -0.03 (-0.88–0.83) | yes |
| Lolwa | Ituri | 36 | 0.4 (0.1–1.6) | 1.08 (0.48–2.27) | 0.58 | -0.12 (-1.15–0.84) | yes |
| Wamba | Haut-Uele | 152 | 40.4 (21.8–63.4) | 1.07 (0.62–1.69) | 0.58 | 0.07 (-0.35–0.48) | yes |
| Tchomia | Ituri | 82 | 0.5 (0.1–2.2) | 1.08 (0.49–2.27) | 0.57 | 0.04 (-0.87–0.92) | yes |
| Kyondo | Nord-Kivu | 44 | 1.2 (0.5–3.0) | 1.07 (0.59–1.93) | 0.57 | -0.07 (-0.6–0.41) | yes |
| Mambasa | Ituri | 76 | 6.7 (1.8–24.3) | 1.08 (0.41–2.46) | 0.56 | 0.22 (-0.87–1.33) | yes |
| Kilo | Ituri | 38 | 0.2 (0.1–0.6) | 1.05 (0.52–2.0) | 0.55 | -0.08 (-0.88–0.62) | yes |
| Vuhovi | Nord-Kivu | 55 | 2.1 (1.0–4.8) | 1.04 (0.6–1.72) | 0.55 | -0.03 (-0.44–0.38) | yes |
| Butembo | Nord-Kivu | 319 | 6.0 (2.7–11.7) | 1.04 (0.58–1.7) | 0.54 | -0.09 (-0.53–0.3) | yes |
| Damas | Ituri | 41 | 0.2 (0.0–1.0) | 1.03 (0.44–2.41) | 0.52 | -0.05 (-1.05–0.9) | yes |
| Komanda | Ituri | 191 | 3.2 (1.0–10.0) | 1.01 (0.43–2.18) | 0.52 | -0.13 (-1.12–0.85) | yes |
| Rwampara | Ituri | 1173 | 8.4 (2.7–20.6) | 1.01 (0.41–1.92) | 0.51 | 0.07 (-0.74–0.85) | yes |
| Katwa | Nord-Kivu | 648 | 8.9 (4.2–17.2) | 1.01 (0.57–1.68) | 0.51 | -0.12 (-0.55–0.27) | yes |
| Isiro | Haut-Uele | 108 | 9.1 (3.5–25.3) | 1.02 (0.53–1.85) | 0.51 | -0.01 (-0.5–0.49) | yes |
| Mandima | Ituri | 362 | 31.3 (8.3–65.0) | 0.98 (0.34–1.79) | 0.49 | 0.28 (-0.83–1.51) | yes |
| Lubero | Nord-Kivu | 36 | 1.4 (0.4–4.9) | 0.98 (0.42–2.12) | 0.48 | 0.03 (-0.59–0.63) | yes |
| Musienene | Nord-Kivu | 154 | 2.3 (0.8–6.0) | 0.99 (0.49–1.87) | 0.48 | -0.08 (-0.61–0.43) | yes |
| Bunia | Ituri | 1914 | 18.2 (5.7–38.5) | 0.97 (0.4–1.72) | 0.47 | 0.12 (-0.59–0.85) | yes |
| Lita | Ituri | 315 | 3.3 (0.8–10.5) | 0.97 (0.34–2.0) | 0.47 | 0.11 (-0.7–0.92) | yes |
| Masereka | Nord-Kivu | 39 | 1.0 (0.4–2.7) | 0.97 (0.48–1.84) | 0.47 | -0.08 (-0.61–0.42) | yes |
| Pawa | Haut-Uele | 106 | 33.8 (16.6–55.4) | 0.98 (0.54–1.62) | 0.47 | 0.0 (-0.39–0.37) | yes |
| Fataki | Ituri | 84 | 0.2 (0.1–0.9) | 0.94 (0.38–2.31) | 0.44 | -0.11 (-0.97–0.74) | yes |
| Bambu | Ituri | 216 | 0.9 (0.3–2.7) | 0.92 (0.41–1.92) | 0.43 | -0.14 (-0.88–0.58) | yes |
| Nizi | Ituri | 832 | 4.1 (1.2–12.0) | 0.87 (0.31–1.84) | 0.38 | 0.05 (-0.61–0.75) | yes |
| Mangala | Ituri | 382 | 1.7 (0.5–6.7) | 0.83 (0.32–2.06) | 0.37 | -0.07 (-0.87–0.76) | yes |
| Mongbwalu | Ituri | 717 | 1.8 (0.4–8.2) | 0.8 (0.27–2.12) | 0.36 | -0.05 (-1.05–0.93) | yes |
| Nia-Nia | Ituri | 334 | 2.6 (0.7–10.9) | 0.72 (0.26–1.97) | 0.31 | -0.12 (-1.27–1.02) | yes |
| Wapinda | Nord-Ubangi | 3 | 100.0 (100.0–100.0) | 1.48 (1.17–1.79) | 0.99 | 0.0 (0.0–0.0) | no |
| Mangobo | Tshopo | 10 | 21.2 (13.4–29.2) | 1.27 (0.92–1.72) | 0.89 | 0.0 (-0.0–0.0) | no |
| Makiso-Kisangani | Tshopo | 33 | 45.8 (37.2–54.9) | 1.24 (0.9–1.67) | 0.87 | 0.0 (-0.0–0.0) | no |
| Kabondo | Tshopo | 10 | 11.3 (6.4–17.3) | 1.22 (0.89–1.66) | 0.86 | 0.0 (-0.0–0.0) | no |
| Bafwasende | Tshopo | 18 | 13.2 (7.8–20.4) | 1.19 (0.86–1.58) | 0.83 | -0.0 (-0.0–0.0) | no |
| Ganga | Bas-Uele | 5 | 42.7 (28.4–59.1) | 1.11 (0.88–1.35) | 0.79 | -0.0 (-0.0–0.0) | no |
| Viadana | Bas-Uele | 6 | 38.3 (21.2–56.1) | 1.08 (0.87–1.31) | 0.75 | -0.0 (-0.0–0.0) | no |
| Gety | Ituri | 11 | 0.3 (0.1–0.6) | 1.09 (0.7–1.57) | 0.63 | -0.0 (-0.0–0.0) | no |
| Manguredjipa | Nord-Kivu | 16 | 0.6 (0.3–1.1) | 1.07 (0.71–1.52) | 0.62 | -0.0 (-0.0–0.0) | no |
| Boga | Ituri | 2 | 0.1 (0.0–0.4) | 1.07 (0.67–1.6) | 0.61 | 0.0 (-0.0–0.0) | no |
| Biena | Nord-Kivu | 24 | 0.8 (0.4–1.4) | 1.06 (0.69–1.52) | 0.6 | -0.0 (-0.0–0.0) | no |
| Aru | Ituri | 15 | 0.2 (0.1–0.5) | 1.03 (0.63–1.53) | 0.55 | -0.0 (-0.0–0.0) | no |
| Ariwara | Ituri | 8 | 0.1 (0.0–0.2) | 1.02 (0.63–1.52) | 0.54 | -0.0 (-0.0–0.0) | no |
| Logo | Ituri | 14 | 0.2 (0.1–0.5) | 1.03 (0.64–1.52) | 0.54 | -0.0 (-0.0–0.0) | no |
| Rimba | Ituri | 19 | 0.2 (0.1–0.4) | 1.02 (0.63–1.52) | 0.54 | -0.0 (-0.0–0.0) | no |
| Mahagi | Ituri | 4 | 0.1 (0.0–0.2) | 1.02 (0.61–1.52) | 0.53 | -0.0 (-0.0–0.0) | no |
| Adja | Ituri | 11 | 0.0 (0.0–0.1) | 1.01 (0.62–1.53) | 0.52 | -0.0 (-0.0–0.0) | no |
| Rungu | Haut-Uele | 6 | 1.1 (0.5–2.0) | 0.99 (0.63–1.51) | 0.49 | -0.0 (-0.0–0.0) | no |
| Aungba | Ituri | 12 | 0.1 (0.0–0.2) | 0.99 (0.6–1.5) | 0.48 | -0.0 (-0.0–0.0) | no |
| Kambala | Ituri | 2 | 0.0 (0.0–0.1) | 0.99 (0.59–1.49) | 0.48 | -0.0 (-0.0–0.0) | no |
| Drodro | Ituri | 15 | 0.2 (0.1–0.4) | 0.97 (0.56–1.47) | 0.47 | 0.0 (-0.0–0.0) | no |
| Alimbongo | Nord-Kivu | 5 | 0.2 (0.1–0.4) | NaN | -0.0 (-0.0–0.0) | no | |
| Goma | Nord-Kivu | 1 | 0.0 (0.0–0.1) | NaN | -0.0 (-0.0–0.0) | no | |
| Kayna | Nord-Kivu | 3 | 0.1 (0.0–0.3) | NaN | -0.0 (-0.0–0.0) | no | |
| Dungu | Haut-Uele | 1 | 0.2 (0.1–0.6) | NaN | -0.0 (-0.0–0.0) | no | |
| Gombari | Haut-Uele | 3 | 0.4 (0.1–0.9) | NaN | -0.0 (-0.0–0.0) | no | |
| Miti-Murhesa | Sud-Kivu | 3 | 100.0 (100.0–100.0) | NaN | 0.0 (0.0–0.0) | no | |
| Lubunga | Tshopo | 1 | 2.2 (0.7–4.9) | NaN | 0.0 (-0.0–0.0) | no | |
| Tshopo | Tshopo | 3 | 2.7 (1.0–5.9) | NaN | -0.0 (-0.0–0.0) | no | |
| Wanie-Rukula | Tshopo | 1 | 2.0 (0.6–4.7) | NaN | -0.0 (-0.0–0.0) | no | |
| Buta | Bas-Uele | 1 | 17.3 (6.6–33.3) | NaN | -0.0 (-0.0–0.0) | no | |
| Bulu | Sud-Ubangi | 2 | 100.0 (100.0–100.0) | NaN | 0.0 (0.0–0.0) | no |
Point prevalence
The zone estimates split the infections in the community at the cut-off in the same way as the province estimates. The point prevalence section of the Methods gives the calculation. The figure and table show the twenty zones with the highest rate in the community per 100,000 residents. Every zone is listed in the fold below the table.
Compute point prevalence by zone
zone_population = Dict(
string(z.province, ".", z.zone) => z.population for z in load_health_zones()
);
zone_prevalence_overview = prevalence_overview(
zone_prevalence(chn_local, zone_inputs, chn_joint),
[zone_population[k] for k in zone_inputs.zone_keys];
labels = zone_inputs.zone_labels, patch = zone_patch
);
zone_prevalence_fig = plot_prevalence_ranking(
zone_prevalence_overview;
patch_labels = zone_inputs.patch_labels, max_rows = 20,
title = "Point prevalence by health zone"
);
MarkdownTable(
prevalence_table(zone_prevalence_overview; max_rows = 20, area = "Zone")| Zone | Population | Infected but not yet symptomatic per 100,000 | Symptomatic, undetected per 100,000 | In the community per 100,000 | Number in the community |
|---|---|---|---|---|---|
| Mandima | 286637 | 56.9 (18.7–144.6) | 64.2 (29.5–128.6) | 121.9 (52.3–268.3) | 350 (150–769) |
| Beni | 428854 | 39.9 (20.0–79.1) | 43.1 (21.6–78.7) | 83.9 (43.3–156.9) | 360 (186–673) |
| Mambasa | 150807 | 22.5 (6.9–80.5) | 23.4 (9.4–60.5) | 47.1 (17.7–137.1) | 71 (27–207) |
| Nia-Nia | 91756 | 15.4 (4.5–55.7) | 23.1 (9.3–54.6) | 38.9 (15.1–107.2) | 36 (14–98) |
| Bunia | 509060 | 17.8 (6.6–43.0) | 20.4 (9.5–42.2) | 38.2 (17.9–81.4) | 194 (91–414) |
| Nizi | 129697 | 16.3 (5.4–48.0) | 19.4 (7.9–45.4) | 36.3 (14.3–90.2) | 47 (19–117) |
| Rwampara | 252324 | 16.6 (6.2–45.1) | 17.6 (7.0–41.5) | 34.4 (14.9–83.8) | 87 (38–211) |
| Lita | 113364 | 14.8 (4.5–48.6) | 17.0 (7.1–43.0) | 32.2 (13.0–87.1) | 37 (15–99) |
| Mabalako | 197136 | 15.0 (5.6–41.7) | 15.4 (6.1–34.1) | 30.3 (12.3–74.0) | 60 (24–146) |
| Mangala | 89713 | 9.8 (3.0–34.4) | 12.9 (5.1–32.3) | 23.1 (8.9–65.2) | 21 (8–59) |
| Kalunguta | 191732 | 9.6 (4.1–23.3) | 9.9 (4.1–22.1) | 19.6 (8.6–44.0) | 38 (17–84) |
| Wamba | 224757 | 9.4 (3.6–23.1) | 9.4 (4.0–20.8) | 18.9 (8.1–43.9) | 43 (18–99) |
| Komanda | 219948 | 7.2 (2.3–24.2) | 8.7 (3.4–21.1) | 16.1 (6.3–44.1) | 35 (14–97) |
| Oicha | 274998 | 7.5 (2.9–21.1) | 7.7 (3.2–17.5) | 15.3 (6.6–37.3) | 42 (18–102) |
| Butembo | 289378 | 7.0 (2.9–18.2) | 7.9 (3.5–17.3) | 14.9 (7.0–34.4) | 43 (20–100) |
| Mongbwalu | 157714 | 5.9 (1.6–25.8) | 8.6 (3.3–23.1) | 14.7 (5.3–47.2) | 23 (8–74) |
| Pawa | 301768 | 5.8 (2.1–15.1) | 6.8 (2.9–14.6) | 12.7 (5.4–29.0) | 38 (16–87) |
| Katwa | 612242 | 5.0 (2.1–12.8) | 5.9 (2.8–12.2) | 11.0 (5.2–23.8) | 67 (32–146) |
| Lolwa | 41314 | 5.3 (1.3–19.3) | 5.5 (1.7–16.9) | 10.9 (3.3–35.5) | 5 (1–15) |
| Vuhovi | 151953 | 4.9 (2.0–13.4) | 5.5 (2.1–12.5) | 10.5 (4.3–25.3) | 16 (6–38) |
Point prevalence in every health zone
| Zone | Population | Infected but not yet symptomatic per 100,000 | Symptomatic, undetected per 100,000 | In the community per 100,000 | Number in the community |
|---|---|---|---|---|---|
| Mandima | 286637 | 56.9 (18.7–144.6) | 64.2 (29.5–128.6) | 121.9 (52.3–268.3) | 350 (150–769) |
| Beni | 428854 | 39.9 (20.0–79.1) | 43.1 (21.6–78.7) | 83.9 (43.3–156.9) | 360 (186–673) |
| Mambasa | 150807 | 22.5 (6.9–80.5) | 23.4 (9.4–60.5) | 47.1 (17.7–137.1) | 71 (27–207) |
| Nia-Nia | 91756 | 15.4 (4.5–55.7) | 23.1 (9.3–54.6) | 38.9 (15.1–107.2) | 36 (14–98) |
| Bunia | 509060 | 17.8 (6.6–43.0) | 20.4 (9.5–42.2) | 38.2 (17.9–81.4) | 194 (91–414) |
| Nizi | 129697 | 16.3 (5.4–48.0) | 19.4 (7.9–45.4) | 36.3 (14.3–90.2) | 47 (19–117) |
| Rwampara | 252324 | 16.6 (6.2–45.1) | 17.6 (7.0–41.5) | 34.4 (14.9–83.8) | 87 (38–211) |
| Lita | 113364 | 14.8 (4.5–48.6) | 17.0 (7.1–43.0) | 32.2 (13.0–87.1) | 37 (15–99) |
| Mabalako | 197136 | 15.0 (5.6–41.7) | 15.4 (6.1–34.1) | 30.3 (12.3–74.0) | 60 (24–146) |
| Mangala | 89713 | 9.8 (3.0–34.4) | 12.9 (5.1–32.3) | 23.1 (8.9–65.2) | 21 (8–59) |
| Kalunguta | 191732 | 9.6 (4.1–23.3) | 9.9 (4.1–22.1) | 19.6 (8.6–44.0) | 38 (17–84) |
| Wamba | 224757 | 9.4 (3.6–23.1) | 9.4 (4.0–20.8) | 18.9 (8.1–43.9) | 43 (18–99) |
| Komanda | 219948 | 7.2 (2.3–24.2) | 8.7 (3.4–21.1) | 16.1 (6.3–44.1) | 35 (14–97) |
| Oicha | 274998 | 7.5 (2.9–21.1) | 7.7 (3.2–17.5) | 15.3 (6.6–37.3) | 42 (18–102) |
| Butembo | 289378 | 7.0 (2.9–18.2) | 7.9 (3.5–17.3) | 14.9 (7.0–34.4) | 43 (20–100) |
| Mongbwalu | 157714 | 5.9 (1.6–25.8) | 8.6 (3.3–23.1) | 14.7 (5.3–47.2) | 23 (8–74) |
| Pawa | 301768 | 5.8 (2.1–15.1) | 6.8 (2.9–14.6) | 12.7 (5.4–29.0) | 38 (16–87) |
| Katwa | 612242 | 5.0 (2.1–12.8) | 5.9 (2.8–12.2) | 11.0 (5.2–23.8) | 67 (32–146) |
| Lolwa | 41314 | 5.3 (1.3–19.3) | 5.5 (1.7–16.9) | 10.9 (3.3–35.5) | 5 (1–15) |
| Vuhovi | 151953 | 4.9 (2.0–13.4) | 5.5 (2.1–12.5) | 10.5 (4.3–25.3) | 16 (6–38) |
| Nyankunde | 94004 | 4.6 (1.4–14.8) | 4.8 (1.7–13.2) | 9.5 (3.4–27.3) | 9 (3–26) |
| Bambu | 136151 | 3.3 (1.1–10.0) | 4.1 (1.6–9.8) | 7.5 (3.1–19.1) | 10 (4–26) |
| Mutwanga | 255278 | 3.6 (1.2–12.5) | 3.5 (1.3–9.7) | 7.2 (2.7–21.9) | 18 (7–56) |
| Manguredjipa | 68014 | 3.1 (1.3–7.4) | 3.3 (1.4–7.4) | 6.5 (2.9–14.3) | 4 (2–10) |
| Tchomia | 83917 | 3.1 (0.8–13.3) | 3.0 (0.9–10.8) | 6.2 (1.8–24.2) | 5 (2–20) |
| Musienene | 348392 | 2.3 (0.8–6.7) | 2.6 (1.0–6.4) | 5.0 (2.0–12.5) | 17 (7–43) |
| Mangobo | 188652 | 2.5 (1.0–6.6) | 2.3 (0.9–5.5) | 4.9 (2.0–11.9) | 9 (4–22) |
| Boma Mangbetu | 230104 | 2.3 (0.9–6.1) | 2.5 (1.0–5.7) | 4.8 (2.0–11.6) | 11 (5–27) |
| Biena | 136159 | 2.1 (0.9–4.9) | 2.3 (1.0–5.1) | 4.5 (2.0–9.6) | 6 (3–13) |
| Kilo | 43139 | 2.1 (0.6–7.4) | 2.3 (0.7–6.6) | 4.4 (1.5–13.9) | 2 (1–6) |
| Bafwasende | 132264 | 2.3 (0.9–6.1) | 2.2 (0.9–5.3) | 4.4 (1.8–11.1) | 6 (2–15) |
| Wapinda | 97712 | 2.3 (0.4–12.2) | 1.9 (0.4–8.5) | 4.2 (0.8–21.3) | 4 (1–21) |
| Makiso-Kisangani | 535798 | 1.9 (0.8–4.5) | 1.8 (0.8–3.9) | 3.8 (1.7–8.2) | 20 (9–44) |
| Kyondo | 270344 | 1.5 (0.5–4.5) | 1.6 (0.6–4.1) | 3.2 (1.3–8.4) | 9 (3–23) |
| Lubero | 377506 | 1.4 (0.4–5.2) | 1.6 (0.5–4.4) | 3.0 (1.0–9.2) | 11 (4–35) |
| Masereka | 298636 | 1.2 (0.4–3.7) | 1.3 (0.5–3.5) | 2.6 (1.0–7.2) | 8 (3–22) |
| Kabondo | 204629 | 1.2 (0.5–3.3) | 1.2 (0.4–2.9) | 2.4 (0.9–6.2) | 5 (2–13) |
| Damas | 94820 | 1.1 (0.3–4.8) | 1.1 (0.3–4.2) | 2.2 (0.6–8.7) | 2 (1–8) |
| Gety | 143442 | 1.0 (0.4–2.5) | 1.1 (0.4–2.6) | 2.1 (0.8–5.0) | 3 (1–7) |
| Boga | 72028 | 1.0 (0.3–2.9) | 1.0 (0.3–3.0) | 2.0 (0.6–5.8) | 1 (0–4) |
| Isiro | 512401 | 0.9 (0.3–3.4) | 1.0 (0.4–2.9) | 2.0 (0.7–6.1) | 10 (4–31) |
| Fataki | 158032 | 0.6 (0.2–2.8) | 0.8 (0.2–2.5) | 1.4 (0.4–5.2) | 2 (1–8) |
| Logo | 216944 | 0.5 (0.2–1.4) | 0.6 (0.2–1.5) | 1.1 (0.4–2.8) | 2 (1–6) |
| Drodro | 199996 | 0.4 (0.2–1.1) | 0.5 (0.2–1.2) | 1.0 (0.4–2.2) | 2 (1–4) |
| Viadana | 187873 | 0.4 (0.2–1.4) | 0.5 (0.2–1.4) | 0.9 (0.3–2.8) | 2 (1–5) |
| Ganga | 235401 | 0.4 (0.1–1.3) | 0.4 (0.1–1.2) | 0.8 (0.3–2.5) | 2 (1–6) |
| Rimba | 222282 | 0.4 (0.1–1.0) | 0.4 (0.2–1.0) | 0.8 (0.3–1.9) | 2 (1–4) |
| Tshopo | 172885 | 0.3 (0.1–1.1) | 0.3 (0.1–1.0) | 0.7 (0.2–2.2) | 1 (0–4) |
| Bulu | 167481 | 0.3 (0.3–0.3) | 0.3 (0.2–0.4) | 0.6 (0.5–0.7) | 1 (1–1) |
| Rungu | 194804 | 0.3 (0.1–0.9) | 0.3 (0.1–0.9) | 0.6 (0.2–1.7) | 1 (0–3) |
| Lubunga | 154712 | 0.3 (0.1–1.0) | 0.3 (0.1–0.9) | 0.6 (0.2–1.9) | 1 (0–3) |
| Aru | 329667 | 0.3 (0.1–0.8) | 0.3 (0.1–0.8) | 0.6 (0.2–1.6) | 2 (1–5) |
| Wanie-Rukula | 155297 | 0.3 (0.1–0.9) | 0.3 (0.1–0.9) | 0.6 (0.1–1.8) | 1 (0–3) |
| Alimbongo | 296821 | 0.2 (0.1–0.6) | 0.2 (0.1–0.6) | 0.5 (0.2–1.2) | 1 (0–4) |
| Adja | 126933 | 0.2 (0.1–0.6) | 0.2 (0.1–0.6) | 0.4 (0.1–1.2) | 1 (0–2) |
| Aungba | 216787 | 0.2 (0.1–0.5) | 0.2 (0.1–0.5) | 0.4 (0.1–1.0) | 1 (0–2) |
| Gombari | 122901 | 0.2 (0.0–0.5) | 0.2 (0.1–0.6) | 0.4 (0.1–1.1) | 0 (0–1) |
| Kambala | 140956 | 0.1 (0.0–0.5) | 0.2 (0.0–0.5) | 0.3 (0.1–1.0) | 0 (0–1) |
| Miti-Murhesa | 303983 | 0.1 (0.1–0.2) | 0.1 (0.1–0.2) | 0.3 (0.2–0.3) | 1 (1–1) |
| Buta | 274964 | 0.1 (0.0–0.5) | 0.1 (0.0–0.5) | 0.3 (0.1–1.0) | 1 (0–3) |
| Kayna | 322628 | 0.1 (0.0–0.4) | 0.1 (0.0–0.4) | 0.3 (0.1–0.8) | 1 (0–2) |
| Mahagi | 247893 | 0.1 (0.0–0.4) | 0.1 (0.0–0.4) | 0.3 (0.1–0.7) | 1 (0–2) |
| Ariwara | 314730 | 0.1 (0.0–0.4) | 0.1 (0.0–0.4) | 0.3 (0.1–0.8) | 1 (0–2) |
| Goma | 282838 | 0.0 (0.0–0.2) | 0.1 (0.0–0.2) | 0.1 (0.0–0.3) | 0 (0–1) |
| Dungu | 307763 | 0.0 (0.0–0.1) | 0.0 (0.0–0.2) | 0.1 (0.0–0.3) | 0 (0–1) |
Composition checks
Whether the model reproduces each zone's observed share of its patch's confirmed cases and deaths is on the in-sample checks page.
Data currency
The zone blocks are the per-zone confirmed-case and confirmed-death tables of the situation reports. A block that stops being picked up shows its last date before the cut-off rather than a flat series.
Zone block currency
# Every zone block whose last vintage falls before the cut-off. The grace
# the national table allows is not applied here: one vintage behind is
# already worth reading.
zone_currency = let
status = stream_report_status(obs; stratum = :zone)
behind = status[[ismissing(d) || d > 0 for d in status.days_since], :]
isempty(behind) ?
Markdown.parse(
"Every zone block reports to the cut-off, $(obs.cutoff)."
) :
MarkdownTable(
DataFrame(
"Block" => behind.label,
"Last reported" => [
ismissing(d) ? "never" : string(d)
for d in behind.last_date
],
"Days before cut-off" => [
ismissing(d) ? "-" : string(d)
for d in behind.days_since
]
)
)
end;Every zone block reports to the cut-off, 2026-10-20.
Health-zone fit diagnostics
Zone fit diagnostics
_zone_per_chain(v) = join(string.(round.(v; sigdigits = 3)), " / ")
function _zone_sampler_row(label, chn)
d = zone_sampler_diagnostics(chn)
return (
fit = label, max_rhat = round(d.max_rhat; digits = 3),
min_ess_bulk = round(d.min_ess_bulk; digits = 0),
min_ess_tail = round(d.min_ess_tail; digits = 0),
divergences = d.n_divergent,
depth_cap = _zone_per_chain(d.depth_cap_fraction),
ebfmi = _zone_per_chain(d.ebfmi),
step_size = _zone_per_chain(d.step_size),
)
end
zone_sampler_table = DataFrame(
[
_zone_sampler_row("health zones", chn_local),
_zone_sampler_row("health zones (frozen)", frozen_local.chn),
]
);
zone_diagnostics = let d = zone_diagnostics_table(chn_local, zone_inputs)
for c in names(d)[3:end]
d[!, c] = round.(d[!, c]; digits = startswith(c, "rhat") ? 3 : 0)
end
DataFrame(
[
n == "walking" ?
"R modelled separately" => [w ? "yes" : "no" for w in d[!, n]] :
n => d[!, n]
for n in names(d)
]
)
end;| fit | max_rhat | min_ess_bulk | min_ess_tail | divergences | depth_cap | ebfmi | step_size |
|---|---|---|---|---|---|---|---|
| health zones | 1.017 | 271 | 359 | 1 | 0.001 / 0.0 | 0.98 / 0.872 | 0.00824 / 0.00848 |
| health zones (frozen) | 1.051 | 45 | 171 | 0 | 0.001 / 0.001 | 0.879 / 0.861 | 0.00701 / 0.00706 |
Per-zone R-hat and effective sample sizes
| zone | R modelled separately | rhat_R_T | ess_bulk_R_T | ess_tail_R_T | rhat_share_T | ess_bulk_share_T | ess_tail_share_T | rhat_delta_T | ess_bulk_delta_T | ess_tail_delta_T |
|---|---|---|---|---|---|---|---|---|---|---|
| Adja | no | NaN | NaN | NaN | 1 | 2004 | 1694 | 1.002 | 894 | 1047 |
| Ariwara | no | NaN | NaN | NaN | 1.001 | 1611 | 1744 | 1.002 | 1646 | 1168 |
| Aru | no | 1 | 1816 | 1364 | 1.001 | 1041 | 1451 | 1.001 | 591 | 957 |
| Aungba | no | NaN | NaN | NaN | 1.002 | 1800 | 1782 | 1 | 642 | 989 |
| Bambu | yes | 1 | 2746 | 1534 | 1 | 2251 | 1453 | 1.003 | 3237 | 1548 |
| Boga | no | NaN | NaN | NaN | 1.002 | 1438 | 1424 | 1.003 | 2217 | 1319 |
| Bunia | yes | 1.002 | 2668 | 1224 | 0.999 | 3169 | 1578 | 1 | 2654 | 1527 |
| Damas | yes | 1 | 2594 | 1564 | 1 | 2643 | 1587 | 1 | 3522 | 1395 |
| Drodro | no | 1 | 2237 | 1273 | 1.001 | 1628 | 1794 | 1.001 | 1499 | 1026 |
| Fataki | yes | 1 | 3085 | 1576 | 1.001 | 2938 | 1754 | 0.999 | 4051 | 1603 |
| Gety | no | NaN | NaN | NaN | 1.001 | 1788 | 1603 | 1.002 | 2187 | 1460 |
| Kambala | no | NaN | NaN | NaN | 1 | 1891 | 1495 | 1 | 1058 | 1111 |
| Kilo | yes | 1.001 | 2096 | 1167 | 1.001 | 1669 | 1315 | 1.002 | 3336 | 1379 |
| Komanda | yes | 1 | 2107 | 1485 | 0.999 | 2072 | 1817 | 0.999 | 2459 | 1577 |
| Lita | yes | 1.001 | 2525 | 1407 | 1 | 2816 | 1602 | 1 | 3019 | 1526 |
| Logo | no | 1 | 1974 | 1401 | 1 | 1346 | 1428 | 1.001 | 679 | 1045 |
| Lolwa | yes | 1 | 2493 | 1521 | 1.004 | 2394 | 1796 | 1 | 3040 | 1664 |
| Mahagi | no | NaN | NaN | NaN | 1.002 | 1859 | 1694 | 1 | 703 | 1092 |
| Mambasa | yes | 0.999 | 2299 | 1512 | 1.001 | 2582 | 1473 | 1.001 | 3236 | 1316 |
| Mandima | yes | 1 | 2959 | 1290 | 1 | 3169 | 1375 | 1.001 | 3367 | 1344 |
| Mangala | yes | 1.002 | 2774 | 1595 | 1.001 | 2745 | 1564 | 1 | 3474 | 1450 |
| Mongbwalu | yes | 1.001 | 2918 | 1494 | 1 | 2661 | 1823 | 1 | 3027 | 1190 |
| Nia-Nia | yes | 1 | 2658 | 1336 | 1 | 2745 | 1613 | 0.999 | 3546 | 1464 |
| Nizi | yes | 1.001 | 2943 | 1450 | 1.002 | 2828 | 1608 | 1 | 2871 | 1649 |
| Nyankunde | yes | 0.999 | 2379 | 1401 | 1 | 2109 | 1571 | 1 | 2709 | 1274 |
| Rimba | no | NaN | NaN | NaN | 1 | 1553 | 1518 | 1.001 | 525 | 946 |
| Rwampara | yes | 1.002 | 3028 | 1484 | 1 | 2615 | 1655 | 1.001 | 2914 | 1362 |
| Tchomia | yes | 1.001 | 2509 | 1412 | 1.001 | 2487 | 1590 | 0.999 | 3289 | 1185 |
| Alimbongo | no | NaN | NaN | NaN | 1 | 2628 | 1662 | 1.001 | 877 | 997 |
| Beni | yes | 1 | 3747 | 1647 | 1.001 | 2436 | 1631 | 1 | 2841 | 1204 |
| Biena | no | NaN | NaN | NaN | 1.001 | 2322 | 1910 | 1.004 | 2076 | 1213 |
| Butembo | yes | 1.004 | 2954 | 1476 | 1 | 3100 | 1749 | 1.001 | 3300 | 1290 |
| Goma | no | NaN | NaN | NaN | 1.001 | 2317 | 1468 | 1.003 | 1045 | 1317 |
| Kalunguta | yes | 1 | 3309 | 1397 | 1.004 | 2375 | 1318 | 1.004 | 3653 | 1167 |
| Katwa | yes | 1.001 | 2646 | 1306 | 1.001 | 2905 | 1264 | 1.006 | 3231 | 1309 |
| Kayna | no | NaN | NaN | NaN | 1.001 | 2320 | 1746 | 1.003 | 706 | 991 |
| Kyondo | yes | 1.002 | 3121 | 1447 | 1.001 | 2448 | 1554 | 1.002 | 3404 | 1357 |
| Lubero | yes | 1 | 3601 | 1284 | 1 | 3636 | 1668 | 1 | 3900 | 1442 |
| Mabalako | yes | 1 | 3733 | 1381 | 1 | 2985 | 1497 | 1 | 3757 | 1329 |
| Manguredjipa | no | NaN | NaN | NaN | 1 | 1800 | 1738 | 1 | 1362 | 803 |
| Masereka | yes | 1 | 3150 | 1472 | 0.999 | 3080 | 1611 | 1.001 | 3774 | 1494 |
| Musienene | yes | 1.002 | 3289 | 1637 | 1 | 2827 | 1648 | 1 | 3313 | 1454 |
| Mutwanga | yes | 1 | 3675 | 1328 | 1 | 2879 | 1721 | 1.002 | 3532 | 1165 |
| Oicha | yes | 1 | 3408 | 1306 | 1 | 3780 | 1483 | 1.001 | 3904 | 1335 |
| Vuhovi | yes | 1.001 | 2728 | 1526 | 1.003 | 2861 | 1923 | 1 | 3489 | 1572 |
| Boma Mangbetu | yes | 1.004 | 2869 | 1425 | 1.001 | 2315 | 1629 | 0.999 | 3555 | 1542 |
| Dungu | no | NaN | NaN | NaN | 1.001 | 2151 | 1965 | 1.001 | 818 | 1173 |
| Gombari | no | NaN | NaN | NaN | 1.001 | 1634 | 1692 | 1.001 | 814 | 1209 |
| Isiro | yes | 1 | 3131 | 1559 | 0.999 | 2530 | 1734 | 1.001 | 3254 | 1513 |
| Pawa | yes | 1 | 3309 | 1416 | 1 | 2841 | 1702 | 1 | 3430 | 1641 |
| Rungu | no | NaN | NaN | NaN | 1 | 1890 | 1935 | 1 | 1316 | 967 |
| Wamba | yes | 1.002 | 3401 | 1406 | 1 | 2706 | 1494 | 0.999 | 3304 | 1446 |
| Miti-Murhesa | no | NaN | NaN | NaN | 1 | 2107 | NaN | NaN | NaN | NaN |
| Bafwasende | no | NaN | NaN | NaN | 1.001 | 1999 | 1860 | 1.002 | 1681 | 965 |
| Kabondo | no | NaN | NaN | NaN | 1.002 | 1656 | 1909 | 1.001 | 2048 | 1220 |
| Lubunga | no | NaN | NaN | NaN | 1 | 2508 | 1718 | 1 | 2265 | 1179 |
| Makiso-Kisangani | no | 1.002 | 2969 | 1418 | 1 | 1665 | 1794 | 1.001 | 1569 | 1096 |
| Mangobo | no | NaN | NaN | NaN | 1.001 | 1832 | 1875 | 1.002 | 1687 | 1242 |
| Tshopo | no | NaN | NaN | NaN | 1.001 | 3582 | 1738 | 1.001 | 2703 | 1283 |
| Wanie-Rukula | no | NaN | NaN | NaN | 1.002 | 2099 | 1182 | 1.003 | 2187 | 1201 |
| Buta | no | NaN | NaN | NaN | 1 | 3204 | 1659 | 1.001 | 2559 | 1257 |
| Ganga | no | NaN | NaN | NaN | 1 | 3283 | 1695 | 1.002 | 2693 | 1304 |
| Viadana | no | NaN | NaN | NaN | 1.001 | 3123 | 1792 | 1.004 | 2293 | 1034 |
| Bulu | no | NaN | NaN | NaN | 1.003 | 2091 | NaN | NaN | NaN | NaN |
| Wapinda | no | NaN | NaN | NaN | 1.002 | 1767 | 1672 | NaN | NaN | NaN |
Reproduction number from the zone stage and the joint fit
# Each patch's implied reproduction number from the joint draws with the
# joint posterior mean generation interval, so the patch line is close to
# the quantity the zone values average to.
zone_grid = zone_inputs.t0:obs.n
_patch_infection_draws = vec(collect(chn_joint[:infections_patch]));
patch_implied_rt = [
let m = Matrix{Float64}(
undef,
length(_patch_infection_draws), obs.n
)
for (i, v) in enumerate(_patch_infection_draws)
I = reshape(Float64.(v), N_PATCHES, obs.n)
m[i, :] .= implied_national_Rt(I[p, :], zone_inputs.g)
end
m
end
for p in 1:N_PATCHES
];
# The implied reproduction number of each row of `draws` (draws × days).
function _implied_rt_matrix(draws::AbstractMatrix)
m = similar(draws, Float64)
for i in axes(draws, 1)
m[i, :] .= implied_national_Rt(view(draws, i, :), zone_inputs.g)
end
return m
end
# The zone stage's deformed patch trajectories on one side and the joint's
# draws on the other, nationally then per patch.
zone_stage_rt = let I = zone_patch_infections(chn_local, zone_inputs)
vcat([_implied_rt_matrix(sum(I))], _implied_rt_matrix.(I))
end;
joint_stage_rt = let draws = _patch_infection_draws
national = Float64[
sum(reshape(Float64.(v), N_PATCHES, obs.n)[:, t])
for v in draws, t in 1:obs.n
]
vcat([_implied_rt_matrix(national)], patch_implied_rt)
end;
zone_meld_rt_fig = plot_rt_zones(
[m[:, zone_grid] for m in zone_stage_rt],
vcat(["National"], zone_inputs.patch_labels),
vcat([N_PATCHES + 1], 1:N_PATCHES);
patch_labels = vcat(zone_inputs.patch_labels, ["National"]),
patch_colours = vcat(BVDOutbreakSize._ZONE_PATCH_COLOURS, [:black]),
dates = grid_date.(zone_grid), as_of_date = obs.cutoff,
top = N_PATCHES + 1, ncols = 3, unit = "patch",
reference_rt = [m[:, zone_grid] for m in joint_stage_rt],
reference_label = "Headline joint fit",
title = "Reproduction number from the zone stage and the joint fit"
);
Health-zone parameters against their priors
Draw from the zone model's prior
prior_chn_zone = zone_prior_draws(zone_inputs);Compute the zone prior and posterior table
# Each scalar zone hyperparameter with its table label and pair-plot axis
# label, kept where both chains carry it: the mixing and correlation
# blocks are sampled only when their inputs are on.
zone_hyper = [
h for h in (
(:region_sd_zone, "Level spread σ_level", "σ_level"),
(:region_halflife_zone, "Deviation half-life (days)", "half-life (days)"),
(:correlation_reference_zone, "Correlation ρ_corr", "ρ_corr"),
(
:zone_ascertainment_sd, "Ascertainment spread σ_ascertainment",
"σ_ascertainment",
),
(
:zone_severity_sd, "Severity spread σ_severity (logit)",
"σ_severity",
),
(:mixing_within_zone, "Within-patch mixing ε_within", "ε_within"),
(:mixing_departure_zone, "Mixing departure τ_mix", "τ_mix"),
(:composition_rho_zone, "Case composition ρ", "ρ"),
(:composition_rho_death_zone, "Death composition ρ_death", "ρ_death"),
)
if BVDOutbreakSize._has_key(chn_local, h[1]) &&
BVDOutbreakSize._has_key(prior_chn_zone, h[1])
]
zone_hyper_keys = first.(zone_hyper)
_hyper_draws(chn, k) = Float64.(vec(collect(chn[k])))
_drift_draws(chn, p) = Float64[
v[p] for v in vec(collect(chn[:region_drift_sd_zone]))
]
function _prior_posterior_row(label, post, prior)
f(x) = string(round(x; sigdigits = 3))
ci(v) = string(
f(median(v)), " (", f(quantile(v, 0.05)), "–",
f(quantile(v, 0.95)), ")"
)
return (
parameter = label, posterior = ci(post), prior = ci(prior),
sd_ratio = round(std(post) / std(prior); digits = 2),
)
end
zone_prior_table = DataFrame(
vcat(
[
_prior_posterior_row(
label, _hyper_draws(chn_local, k),
_hyper_draws(prior_chn_zone, k)
)
for (k, label, _) in zone_hyper
],
[
_prior_posterior_row(
"Drift scale σ_δ ($(zone_inputs.patch_labels[p]))",
_drift_draws(chn_local, p), _drift_draws(prior_chn_zone, p)
)
for p in eachindex(zone_inputs.patch_labels)
]
)
);
# The pair plot's axes take the symbols alone.
zone_hyper_pair_fig = plot_pair(
chn_local, zone_hyper_keys;
prior = prior_chn_zone,
labels = Dict(k => sym for (k, _, sym) in zone_hyper)
);| parameter | posterior | prior | sd_ratio |
|---|---|---|---|
| Level spread σ_level | 0.463 (0.0732–0.856) | 0.198 (0.0214–0.597) | 1.33 |
| Deviation half-life (days) | 16.3 (10.0–26.3) | 43.8 (16.2–127.0) | 0.14 |
| Correlation ρ_corr | 1.7e-5 (1.73e-7–0.000586) | 0.365 (0.0416–0.849) | 0 |
| Ascertainment spread σ_ascertainment | 0.176 (0.0415–0.28) | 0.101 (0.0181–0.422) | 0.36 |
| Severity spread σ_severity (logit) | 0.182 (0.017–0.411) | 0.0951 (0.00718–0.273) | 1.45 |
| Within-patch mixing ε_within | 0.079 (0.0427–0.135) | 0.0366 (0.00306–0.13) | 0.67 |
| Mixing departure τ_mix | 0.465 (0.0544–0.941) | 0.348 (0.0327–0.98) | 0.88 |
| Case composition ρ | 0.0287 (0.0257–0.0319) | 0.0328 (0.0243–0.0426) | 0.33 |
| Death composition ρ_death | 0.0402 (0.0344–0.0465) | 0.0516 (0.0381–0.0701) | 0.37 |
| Drift scale σ_δ (Ituri) | 0.47 (0.363–0.631) | 0.11 (0.0594–0.203) | 1.76 |
| Drift scale σ_δ (Nord-Kivu) | 0.304 (0.209–0.439) | 0.107 (0.0598–0.199) | 1.55 |
| Drift scale σ_δ (Haut-Uele) | 0.28 (0.178–0.446) | 0.111 (0.0587–0.199) | 1.85 |
| Drift scale σ_δ (Sud-Kivu) | 0.109 (0.0573–0.205) | 0.113 (0.0584–0.191) | 1.12 |
| Drift scale σ_δ (Tshopo) | 0.109 (0.0595–0.196) | 0.109 (0.0616–0.203) | 1.01 |
| Drift scale σ_δ (Bas-Uele) | 0.109 (0.057–0.201) | 0.106 (0.0579–0.202) | 1.02 |
| Drift scale σ_δ (Sud-Ubangi) | 0.109 (0.0613–0.192) | 0.107 (0.0585–0.203) | 0.96 |
| Drift scale σ_δ (Nord-Ubangi) | 0.109 (0.0591–0.199) | 0.11 (0.0591–0.207) | 0.93 |
Zone hyperparameter pair plot (prior overlaid)

Change over the past week
How each zone's reproduction number has moved between the fit a week earlier and the current one is on the in-sample checks page.
Saving zone assets
The dashboard map reads the per-zone estimates and time series written here, and the summary dashboard's health-zone table is built from them. The zone forecast assets are written by the health-zone forecasts and health-zone forecast evaluation pages.
Write the zone dashboard and release assets
dashboard_dir = joinpath(
pkgdir(BVDOutbreakSize), "docs", "src", "summary_assets"
)
mkpath(dashboard_dir)
# The health-zone maps, the weekly cases against R and the one-week zone forecast for the summary
# dashboard, and the per-zone estimates the interactive map reads: one row per zone keyed as the
# geojson keys it, with the cases and deaths to date, the reproduction
# number and the chance it exceeds one, the one-week forecast and the
# share of the patch's infections, whether the zone's reproduction number
# is modelled separately (`walking`) and the data cut-off. A zone below the
# reporting floor carries no reproduction number.
CairoMakie.save(joinpath(dashboard_dir, "zone_rt_map.png"), zone_map_fig)
CairoMakie.save(
joinpath(dashboard_dir, "cases_rt_zones.png"),
plot_cases_rt(
zone_cases_rt; patch_labels = zone_inputs.patch_labels, top = 12,
unit = "health zone", size = (680, 560), fontsize = 12
)
)
_zone_deaths = [
let h = obs.zone_death_history
haskey(h, prov) && haskey(h[prov], z) &&
!isempty(h[prov][z].counts) ? Int(h[prov][z].counts[end]) :
0
end
for (prov, z) in zip(
zone_inputs.zone_province,
zone_inputs.zone_names
)
]
_zone_rt_stat(f) = [isempty(r) ? missing : f(r) for r in zone_RT_finite]
zone_estimates = DataFrame(
zone = zone_map_keys,
label = zone_inputs.zone_labels, province = zone_inputs.zone_province,
patch = zone_inputs.patch_labels[zone_patch],
cases = zone_inputs.cumulative, deaths = _zone_deaths,
R_T_median = _zone_rt_stat(median),
p_rt_above_one = [
isempty(r) ? missing : mean(r .> 1) for r in zone_RT_finite
],
R_T_lower = _zone_rt_stat(r -> quantile(r, 0.05)),
R_T_upper = _zone_rt_stat(r -> quantile(r, 0.95)),
forecast_median = [median(v) for v in zone_fc_draws.zones],
forecast_lower = [quantile(v, 0.05) for v in zone_fc_draws.zones],
forecast_upper = [quantile(v, 0.95) for v in zone_fc_draws.zones],
share_median = [median(v) for v in zone_share_T],
share_lower = [quantile(v, 0.05) for v in zone_share_T],
share_upper = [quantile(v, 0.95) for v in zone_share_T],
p_ge_1 = zone_fc_probs[:, 1], p_ge_5 = zone_fc_probs[:, 2],
p_ge_10 = zone_fc_probs[:, 3], p_ge_20 = zone_fc_probs[:, 4],
cases_last_7 = zone_recent[7], cases_last_14 = zone_recent[14],
cases_last_28 = zone_recent[28],
last_case_date = [ismissing(d) ? "" : string(d) for d in zone_last_case],
walking = Int.(zone_inputs.walking),
as_of = fill(string(obs.cutoff), length(zone_map_keys))
)
CSV.write(joinpath(dashboard_dir, "zone_estimates.csv"), zone_estimates)
# Each zone's daily reproduction number, weekly allocated confirmed cases
# and weekly confirmed deaths, for the dashboard map's detail column. A
# zone's deaths are its own cumulative series, so each zone is binned on
# its own vintages.
_with_series(t, s) = (t[!, :series] .= s; t)
_zone_weekly_deaths = let h = obs.zone_death_history
out = DataFrame(area = String[], date = Date[], count = Int[])
for (prov, z, key) in zip(
zone_inputs.zone_province, zone_inputs.zone_names, zone_map_keys
)
haskey(h, prov) && haskey(h[prov], z) &&
!isempty(h[prov][z].counts) || continue
c = h[prov][z].counts
append!(
out,
weekly_count_table(
h[prov][z].days, reshape([c[1]; max.(diff(c), 0)], 1, :),
[key]; cutoff = obs.cutoff, n = obs.n
)
)
end
out
end
CSV.write(
joinpath(dashboard_dir, "zone_timeseries.csv"),
vcat(
_with_series(
rt_quantile_table(
zone_rt_traj, zone_map_keys; cutoff = obs.cutoff,
n = obs.n, from = zone_inputs.t0
), "rt"
),
_with_series(
weekly_count_table(
zone_inputs.days, zone_inputs.counts, zone_map_keys;
cutoff = obs.cutoff, n = zone_inputs.n
), "cases"
),
_with_series(_zone_weekly_deaths, "deaths");
cols = :union
)
)
open(joinpath(dashboard_dir, "zone_headline.md"), "w") do io
print(io, zone_headline(zone_estimates; top = 10))
end
# The zone estimates and time series into the release outputs, so each
# results release carries the dashboard map's zone inputs at its cut-off.
output_dir = get(
ENV, "BVD_OUTPUT_DIR",
joinpath(pkgdir(BVDOutbreakSize), "output")
)
mkpath(output_dir)
for f in ("zone_estimates.csv", "zone_timeseries.csv")
cp(joinpath(dashboard_dir, f), joinpath(output_dir, f); force = true)
endThe full analysis code, data and model definitions are in the epiforecasts/BVDOutbreakSize repository.