5  Activation frequency

If the candidate trigger were run over 2018–2023, how often would it have fired, where, and what return period does that imply? Frequency is what links a trigger to a funding envelope.

5.1 Activations per year

Show code
by_yr_state = (
    act.groupby(["year", "state"]).size().unstack(fill_value=0).reindex(columns=U.STATES, fill_value=0)
    .reindex(range(2018, 2024), fill_value=0)
)
fig, ax = plt.subplots(figsize=(10, 4.5))
bottom = np.zeros(len(by_yr_state))
for st in U.STATES:
    ax.bar(by_yr_state.index.astype(str), by_yr_state[st], bottom=bottom, label=st, color=U.STATE_COLORS[st])
    bottom += by_yr_state[st].values
ax.set_ylabel("LGA activations")
ax.set_title("Trigger activations per year")
ax.legend(title="State", ncol=3)
plt.tight_layout()
plt.show()
Figure 5.1: LGA-year activations per year, coloured by state. Firing concentrates in the outbreak years; 2020 and 2023 produce none (2020 has no data, 2023 is truncated).

5.2 Where it fires

Show code
counts = (
    act.groupby(["ADM2_EN", "state"]).agg(activations=("year", "nunique"),
                                           years=("year", lambda s: ", ".join(map(str, sorted(s.unique())))))
    .reset_index().sort_values("activations", ascending=False)
)
pcode_by_name = lw[["ADM2_EN", "ADM2_PCODE"]].drop_duplicates().set_index("ADM2_EN")["ADM2_PCODE"]
counts["priority"] = counts["ADM2_EN"].map(pcode_by_name).isin(U.PRIORITY_LGAS)
counts.columns = ["LGA", "State", "Activations", "Years", "Priority LGA"]
counts.head(15).style.hide(axis="index")
Table 5.1: LGAs by number of activations over 2018–2023. Priority LGAs are marked.
LGA State Activations Years Priority LGA
Damaturu Yobe 3 2018, 2021, 2022 False
Bama Borno 2 2019, 2022 True
Gujba Yobe 2 2018, 2022 False
Konduga Borno 2 2021, 2022 False
Jere Borno 2 2021, 2022 False
Fika Yobe 1 2022 False
Fufore Adamawa 1 2018 False
Dikwa Borno 1 2022 True
Gulani Yobe 1 2022 False
Gwoza Borno 1 2021 False
Bayo Borno 1 2022 False
Chibok Borno 1 2018 False
Mafa Borno 1 2021 False
Magumeri Borno 1 2018 False
Maiha Adamawa 1 2018 False

Across the period the trigger produces 27 LGA-year activations in 21 distinct LGAs. Most LGAs fire in a single outbreak year; a few (Damaturu, Bama, Jere, Konduga, Gujba) fire in more than one.

5.3 Implied return period

A crude return period divides the LGA-years observed by the activations seen. Because 2020 is a data gap, we compute it two ways: over all six years, and over the four well-reported years (2018, 2019, 2021, 2022).

Show code
def rp(years):
    a = act[act["year"].isin(years)]
    lga_years = n_lgas * len(years)
    rate = len(a) / lga_years
    return len(a), lga_years, rate, (1 / rate if rate else np.nan)

rows = []
for label, yrs in [
    ("All years (2018–2023)", list(range(2018, 2024))),
    ("Well-reported yrs (2018,19,21,22)", [2018, 2019, 2021, 2022]),
]:
    a, ly, rate, per = rp(yrs)
    rows.append([label, a, ly, f"{rate:.3f}", f"~1 in {per:.0f} LGA-years"])
rpt = pd.DataFrame(rows, columns=["Exposure window", "Activations", "LGA-years", "Activation rate", "Return period"])
rpt.style.hide(axis="index")
Table 5.2: Implied per-LGA return period under two exposure assumptions. Excluding the 2020 gap year gives the more honest figure.
Exposure window Activations LGA-years Activation rate Return period
All years (2018–2023) 27 330 0.082 ~1 in 12 LGA-years
Well-reported yrs (2018,19,21,22) 27 220 0.123 ~1 in 8 LGA-years

Read the return period per-LGA: roughly 1 activation per 12–15 LGA-years — i.e. a given LGA triggers about once a decade, but with 55 LGAs in scope the system fires several times in any real outbreak year. That is the tension a framework has to price: the per-LGA event is rare, but the portfolio-wide event (at least one LGA firing somewhere in BAY in a bad year) is close to annual.

5.4 Priority-LGA focus

The earlier exploration designated four priority LGAs — Bama and Numan (priority-1), Dikwa and Ngala (priority-2). How does the trigger behave if scope is narrowed to just these?

Show code
prio_act = act[act["ADM2_PCODE"].isin(U.PRIORITY_LGAS)].copy()
if len(prio_act):
    p = prio_act[["ADM2_EN", "state", "year", "week", "cases"]].sort_values("week")
    p["week"] = p["week"].dt.date
    p.columns = ["LGA", "State", "Year", "Activation week", "Cases that week"]
    display_p = p.style.hide(axis="index")
else:
    display_p = "No activations in the four priority LGAs under the baseline trigger."
display_p
Table 5.3: Activations restricted to the four priority LGAs.
LGA State Year Activation week Cases that week
Bama Borno 2019 2019-07-15 90
Dikwa Borno 2022 2022-09-26 397
Bama Borno 2022 2022-10-03 189
Ngala Borno 2022 2022-10-10 347

Restricting to the four priority LGAs yields 4 activations over the period. A priority-only trigger is far quieter — which controls cost, but at the risk of missing high-burden non-priority LGAs (e.g. the Maiduguri-area and Yobe LGAs that dominate the burden map). The priority-LGA rationale was never documented in the earlier work and deserves an explicit, burden-based justification before it anchors a framework.