skip to content
all projects
StormGeo · Data Scientist · 2018–2021

Storm Risk Modelling

A postcode-level storm-impact model fusing satellite, radar, and historical claims data to score damage risk 72 hours ahead — contributing to £1.5M in avoided insurance claims.

status delivered
domain geospatial ML · time series
lead time 72h · postcode granularity
stack Python · PyTorch · ARIMA · Prophet · GeoPandas · Tableau
OVERVIEW

What the model does

The model predicts storm damage severity at postcode level, 72 hours ahead, so insurers can prepare claims teams, route field assessors, and warn policyholders. It fuses satellite and radar imagery with historical claims outcomes to learn where general weather translates into actual damage.

ENGINEERING GOAL
Move from general regional weather warnings to postcode-level damage-severity prediction with enough lead time to change operational and pricing decisions.
PROBLEM

Why weather models weren't enough

A wind-speed forecast isn't a damage forecast — the same storm causes very different losses across terrain, building stock, and exposure. Existing models gave coarse regional warnings; insurers needed spatially-resolved severity tied to what actually drives claims.

ARCHITECTURE

System architecture

Ingestion pipelines clean and spatially align 2TB+/month of climate data; a CNN fuses imagery with engineered geo-features, trained against historical claims as ground truth; outputs are rendered as a postcode risk map driving insurer alerts.

Satellite + Radar imagery feeds Geo Pipeline clean + align Claims Join historical labels CNN PyTorch fusion 72h Forecast lead time Postcode Risk severity map Insurer Alerts ops routing
Figure 1 · StormGeo risk pipeline — multi-source fusion through a CNN trained on claims outcomes into postcode-level alerts.
FUSION

Data fusion & validation

The signal is in the fusion: imagery alone predicts weather, not damage. Joining historical claims as labels teaches the model where weather becomes loss. Forecasts are validated on held-out storm events before informing pricing.

multi-source fusion inference (representative) representative pattern
def score_postcode(pc: Postcode, t0: datetime) -> RiskScore:
    imagery = align(satellite.at(pc, t0), radar.at(pc, t0))  # spatial alignment
    geo = geo_features(pc)                                     # terrain, exposure
    # CNN trained on historical claims as ground truth
    return cnn.predict(stack(imagery, geo), horizon="72h")
CLAIMS AS GROUND TRUTH
Training against real claims outcomes — not just weather severity — is what let the model predict damage rather than wind. Validated 72h-ahead accuracy of ~91% across 12 regions.
DESIGN DECISIONS

Architecture decision records

ADR-01

Train on claims, not weather severity

accepted

Decision: use historical claims as labels rather than predicting weather intensity.

Consequence: the model predicts damage — the quantity insurers actually price — capturing terrain and exposure effects a weather model misses.

ADR-02

Postcode granularity over regional

accepted

Decision: resolve risk to postcode rather than region.

Consequence: actionable routing of assessors and targeted policyholder warnings, at the cost of heavier geo-feature engineering.

OUTCOMES

Outcomes

CLAIMS AVOIDED
£1.5M
ACCURACY
91%
LEAD TIME
72h
REGIONS
12
ON THE FIGURES
Metrics reflect the delivered model; code shown is a representative pattern, not proprietary implementation.