Both architectures were evaluated on the same dataset. The Random Forest serves as the production predictor due to its superior AUC, simpler feature requirements, and no need for sequence context at inference time. The LSTM was trained as a sequence-aware baseline on 17 raw and derived physical features, without the full 39-feature set used by the RF.
The LSTM trained on 17 physical features (raw + rolling + gradients) achieves AUC 0.678 vs. 0.836 for the RF on 39 features. The gap is expected: the RF receives pre-computed 7-day rolling means, 3-day lags, and a dry index specifically tuned for Seville's climate — temporal context that the LSTM must reconstruct from raw sequences. With 24k days of training data the LSTM saturates quickly. The AUC-PR plateaued around epochs 14-15 and early stopping triggered at epoch 40.
Time-series aware 5-fold cross-validation (TimeSeriesSplit). Folds are strictly
sequential — no future data leaks into any training window. The
event rate is 5.0%, addressed with
class_weight='balanced_subsample'
across 400 estimators.
| Fold | F1 | ROC-AUC |
|---|---|---|
| 1 | 0.043 | 0.806 |
| 2 | 0.127 | 0.773 |
| 3 | 0.190 | 0.888 |
| 4 | 0.156 | 0.825 |
| 5 | 0.229 | 0.886 |
| Mean | 0.149 | 0.836 |
AUC improves in later folds (0.773 → 0.886), consistent with the model learning Seville's seasonal extreme patterns more reliably as the training window grows. Low F1 is expected at 5% event rate under strong class imbalance — AUC 0.836 is the operative metric.
Mean absolute SHAP values via TreeExplainer over 200 training samples. Minimum daily pressure ranks first — sharp pressure drops are the clearest precursor of frontal systems. Temperature maximum ranks second, directly defining heat and cold thresholds. Cosine day-of-year ranks third, confirming strong seasonality.
The dry index (thermal range × (100 − mean humidity)) is the most Seville-specific feature: wide diurnal swings with very low humidity are the signature precondition for the city's most dangerous heat events.
Two-layer LSTM with LayerNorm and a GELU MLP head. LayerNorm was chosen over BatchNorm for stability with short sequences and variable batch sizes. Focal Loss (α=0.80, γ=1.5) shifts gradient weight toward the rare positive class. Trained with AdamW (lr=1×10⁻⁴) and cosine annealing.
# AtmosphericLSTM — forward pass Input → (batch, 14, 17) # 14-day window × 17 features LSTM₁ → hidden=128 # short-range synoptic patterns LSTM₂ → hidden=64 # regime-level compression LayerNorm → (batch, 64) # last timestep only Linear(64→32) + GELU + Dropout(0.25) Linear(32→1) + Sigmoid # P(extreme event tomorrow)
Early stopping triggered at epoch 40 (patience 25 on AUC-PR). AUC-PR peaked at epoch 15 (0.121) and did not improve further, while ROC-AUC continued rising slowly. This plateau is characteristic of Focal Loss on highly imbalanced data: once the model stops finding new positive examples to focus on, the precision-recall curve saturates.
The dataset covers two periods: 1940–1990 (51 years of pre-industrial and early-industrial baseline) and 2010–2024 (15 years of contemporary climate with documented warming trend). This split allows the model to learn both long-run climatological distributions and the contemporary extreme event shift.
Copernicus CDS API via cdsapi. 7 variables
at 06:00 / 12:00 / 18:00 UTC. Area: 38°N–36.5°N, 6.5°W–5°W.
Two NetCDF4 series — instantaneous (temp, pressure, wind, dewpoint)
and accumulated (precipitation, cloud cover).
K → °C · Pa → hPa · precipitation diff × 1000 (m→mm) · wind → speed via √(u²+v²) · humidity from dewpoint via Magnus formula. Resampled to daily: Tmax, Tmin, Tmean, Pmean, Pmin, windmax/mean, precipsum, humiditymax/mean, cloudmean.
ERA5 underestimates Tmax in Seville by ~2.5°C due to the 1.5° grid resolution. A constant +2.5°C correction is applied to all temperature fields before feature engineering and label thresholding.
RF (39 features): raw daily aggregates · 3/7-day rolling means ·
1/2/3-day lags · pressure and temperature gradients · sin/cos seasonality ·
thermal range, heat intensity, pressure deficit, humidity range,
wind spike, dry index, normalized pressure.
LSTM (17 features): 9 raw physical variables + 8 derived:
temp_ma3/ma7, pressure_ma3, pressure_grad, wind_speed, wind_ma3,
precip_ma3, dry_index. Scaled with StandardScaler fitted on train
days only — before sequence construction to avoid overlap bias.
Binary target: extreme event tomorrow (shift −1 day). Triggers: heat Tmax ≥ 38°C · cold Tmax ≤ 10°C · wind ≥ 8 m/s · rain ≥ 1 mm/day. OR logic. Event rate: 5.0%.
RandomForestClassifier( n_estimators=400, max_depth=10, min_samples_leaf=4, class_weight="balanced_subsample", # resamples weights per tree random_state=42, n_jobs=-1 )
No feature scaling — RF is scale-invariant. The feature list is saved to
features.pkl alongside the model to guarantee
column order consistency at inference time.
The RF requires temporal features (rolling means, lags) that cannot be computed from a single API snapshot. The system accumulates one daily record per calendar day in a CSV committed to the repository.
The production model outputs a calibrated probability through
predict_proba(). That probability drives the
visual warning system. The event type is a secondary heuristic label derived
from the current feature row. When risk is active but no specific type crosses
its physical threshold, the interface displays a generic extreme-event signal
instead of over-claiming heat, cold, rain, or wind.
min_periods=1. Lag features partially filled with current value
as proxy.