This appendix documents the early work scoping ADAM (WFP) and GDACS as exposure sources for CERF Rapid Response allocation modelling. Both datasets were considered alongside the OCHA in-house exposure product; this chapter focuses on the question “can we use them to drive RR allocation modelling given CERF’s 2006 to 2023 temporal coverage?” Because of the limited overlap, the answer was no, and the main analysis uses the OCHA exposure dataset instead (see Data & Merge, which covers the storm ID matching that links CERF allocations to exposure).
import ocha_stratus as stratusimport pandas as pdimport matplotlib.pyplot as pltimport matplotlib.ticker as mtickerimport numpy as npfrom dotenv import load_dotenvload_dotenv()df_cerf = stratus.load_csv_from_blob("ds-storm-impact-harmonisation/processed/cerf-storms-with-sids-2024-02-27.csv")df_adam = stratus.load_csv_from_blob("ds-cyclone-exposure/adam_historical_national_exposure.csv")df_gdacs = stratus.load_csv_from_blob("ds-cyclone-exposure/gdacs_historical_national_exposure.csv")
11.1 ADAM (WFP)
ADAM provides population exposure at three wind speed thresholds: 60, 90, and 120 km/h.
GDACS has rows going back to 2015, but the actual population exposure values are missing for 2016–2021. This is likely a pipeline issue — the storm metadata was ingested but exposure calculations were not run for those years.
Code
seasons =sorted(df_gdacs["season"].unique())stats = []for s in seasons: sub = df_gdacs[df_gdacs["season"] == s] stats.append({"season": int(s),"total": len(sub),"pop_34kt_filled": sub["pop_34kt"].notna().sum(),"pop_64kt_filled": sub["pop_64kt"].notna().sum(), })stats_df = pd.DataFrame(stats)x = np.arange(len(stats_df))width =0.25fig, ax = plt.subplots(figsize=(12, 5))bars1 = ax.bar(x - width, stats_df["total"], width, label="Total rows", color="#d9d9d9", edgecolor="#666")bars2 = ax.bar(x, stats_df["pop_34kt_filled"], width, label="pop_34kt filled", color="#4292c6", edgecolor="#666")bars3 = ax.bar(x + width, stats_df["pop_64kt_filled"], width, label="pop_64kt filled", color="#ef6548", edgecolor="#666")for bars in [bars1, bars2, bars3]:for bar in bars: h = bar.get_height()if h >0: ax.text(bar.get_x() + bar.get_width() /2, h +1, str(int(h)), ha="center", va="bottom", fontsize=8)ax.set_xlabel("Season")ax.set_ylabel("Number of rows (storm × country)")ax.set_title("GDACS Historical National Exposure — Data Completeness")ax.set_xticks(x)ax.set_xticklabels(stats_df["season"].astype(str))ax.legend()ax.annotate("No exposure values\n2016–2021", xy=(3.5, 50), fontsize=11, ha="center", color="red", fontweight="bold", bbox=dict(boxstyle="round,pad=0.3", facecolor="#fff3f3", edgecolor="red", alpha=0.8),)plt.tight_layout()plt.show()
GDACS exposure data completeness by season. Rows exist for 2016–2021 but contain no exposure values.
The pop_64kt column is also sparse even in years with data — only filled for the most intense storms where winds exceeded 64 kt in populated areas.
2015–2025 rows, 2022–2025 usable (60 storms with values)
CERF × Exposure matches
~1 row
Key blockers:
GDACS 2016–2021 gap — rows exist but exposure values are null (pipeline issue?)
ADAM only starts 2023 — no overlap with bulk of CERF data (2006–2022)
CERF missing sids — 29% of allocations can’t be joined at all
Filling the GDACS 2016–2021 exposure gap would immediately unlock 6 more storms (11 CERF allocation rows). Extending exposure estimates further back to 2006 would maximise the usable dataset.