--- license: mit language: - en - fr library_name: sklearn pipeline_tag: tabular-classification tags: - wildfire - fire-weather-index - tabular-classification - scikit-learn - logistic-regression - france - var - open-data --- # firepredict: daily wildfire-start risk for the communes of the Var and the Bouches-du-Rhône (France) > **Versions in this repository.** The folder [`v4/`](v4/) holds `small_commune_v4`, the current model (Var and Bouches-du-Rhône), described in the section "Version 4" below. The root files are `small_commune_v2` (the Var only) and [`v3/`](v3/) holds `small_commune_v3`; both are kept frozen for comparison on 2026. A small logistic regression that gives each of the 153 communes of the Var (83), France, a daily chance that a fire of 0.1 ha or more starts there. It is built only from open data. Code, data pipeline and full report: [github.com/guillaumegay13/firepredict](https://github.com/guillaumegay13/firepredict). > **Research preview.** This is not an official warning tool. Fire danger warnings in France are issued by Météo-France and the prefectures. The every-year results below are exploratory (see [Limitations](#limitations)); the clean test is the 2026 one. ## What it predicts For one commune and one target day: the chance that **at least one fire of 0.1 ha or more starts in the commune on the target day**. - The forecast is issued on the morning of the day before the target day. - It uses data up to two days before the target day (the last complete day of station data). - No input uses recent fire records. BDIFF publishes a year's fires months later, so a live forecast cannot know them. Automated tests in the GitHub repository check the time rules and that no input changes when fire records change. Version: `small_commune_v2`, frozen on 9 November 2026 (git commit `5fa2bc1`), trained on every labelled year. ## Inputs A scikit-learn pipeline: `StandardScaler` then `LogisticRegression` (default settings, `max_iter=1000`). Five inputs, in this order: | Input | Definition | Unit / transform | Standardised weight | |---|---|---|---| | `log_usual_rate` | Commune's usual fire rate: average number of days per year with at least one fire start of 0.1 ha or more, 2006–2025 without 2024 (`usual_rates.csv`) | log(rate + 0.01) | +0.589 | | `log_local_fwi` | Fire Weather Index two days before the target day, from the commune's nearest Météo-France stations | log(1 + FWI), FWI has no unit | +0.858 (strongest) | | `local_wb_30d` | Rain minus potential evaporation (Météo-France ETP) over the 30 days that end two days before the target day, nearest stations | mm, negative = dry | −0.562 | | `orange_period` | Target day in February or March (brush burning allowed under conditions) | 0 / 1 | +0.230 | | `red_period` | Target day in June to September (fire use banned) | 0 / 1 | −0.261 | A missing input is replaced with its training median (`fill_values` in `metadata.json`). The FWI here is an approximation computed from daily station values (daily maximum temperature, minimum humidity, mean wind, daily rain), not the official noon-based product. ## How to use ```bash uv run --with scikit-learn==1.9.1 --with joblib --with pandas python example.py ``` `example.py` checks the SHA-256 fingerprints, loads the model and computes the chance for example rows. The pipeline was saved with **scikit-learn 1.9.1**; older versions (for example 1.7.2) fail to load it. Joblib files are pickles: load only files whose fingerprint you trust (`model_sha256` in `metadata.json`). ```python import joblib, json, numpy as np, pandas as pd model = joblib.load("model.joblib") meta = json.load(open("metadata.json")) rates = pd.read_csv("usual_rates.csv", dtype={"insee": str}).set_index("insee").usual_rate X = pd.DataFrame([{ "log_usual_rate": np.log(rates["83137"] + 0.01), # Toulon "log_local_fwi": np.log1p(35.0), # FWI two days before the target day "local_wb_30d": -140.0, # mm over 30 days "orange_period": 0, "red_period": 1, # target day in July }])[meta["inputs"]].fillna(pd.Series(meta["fill_values"])) print(model.predict_proba(X)[0, 1]) # about 0.0034 ``` To build the inputs from raw data (weather download, station matching, FWI), use the GitHub repository: `firepredict.model_communes.build_panel` and `firepredict.freeze.predict`, or `uv run python -m firepredict.pipeline forecast` for today's forecast. ## Files | File | Content | |---|---| | `model.joblib` | The fitted pipeline (SHA-256 `a1e64b40ed3aa7c274d4b2ec01f24e93a80beb976c3e03ce6d2a3e837056b243`) | | `usual_rates.csv` | Usual fire rate per commune (INSEE code), used for forecasts | | `metadata.json` | Inputs, weights, fill values, training years and rows, raw-data fingerprints, file fingerprints | | `forecasts_2026.csv` | Retrospective 2026 test forecasts: `target_day`, `insee`, `chance` (47,889 rows) | | `example.py` | Load the model and compute a chance | | `LICENSE` | MIT licence of the code and model | ## Training data - **Fire records:** BDIFF (Base de Données sur les Incendies de Forêts en France), Var, 2006–2025 without 2024. Only fires of 0.1 ha or more are counted. - **Weather:** Météo-France daily climatological data, all Var stations. - **Burning calendar:** Var prefectoral orders on fire use of 16 May 2013 and 2 November 2025. 1,052,640 commune-days, of which 902 positive commune-days (from 910 fires). A positive commune-day is a day on which at least one fire of 0.1 ha or more started in the commune. ## Evaluation Leave-one-year-out over 19 years (2006–2025 without 2024): each year is hidden in turn, the model learns on the other years, then forecasts the hidden year. The unit is a positive commune-day (902 in total). Ranges are 95% ranges from resampling years. **Against two references:** | Reference | ROC AUC (reference → model) | Years better | 15 communes/day: caught | 10% of commune-days: caught | |---|---|---|---|---| | FWI alone (Var) | 0.718 → 0.782 | 19 / 19 | 85 → 213, +151% (+104% to +221%) | 230 → 354, +54% (+33% to +74%) | | Local FWI alone | 0.728 → 0.782 | 18 / 19 | 123 → 213, +73% (+46% to +111%) | 256 → 354, +38% (+20% to +58%) | | FWI × commune history | 0.757 → 0.782 | 16 / 19 (p < 0.001) | 222 → 213, −4% (−10% to +2%) | 307 → 354, +15% (+2% to +30%) | - **Against the FWI alone**, the model is clearly better. - **Against the FWI multiplied by each commune's usual fire rate**, the model ranks commune-days better, but the gain in fires caught is about zero at practical warning levels (15 communes a day: −4%, −10% to +2%). - **On its very top warnings the model does worse than FWI × history:** 59 against 76 at the 1% most alarming commune-days. Its pooled PR AUC is also lower (0.0030 against 0.0034). **What each input adds** (nested, ROC AUC): | Inputs | ROC AUC | |---|---| | Commune history + local FWI | 0.767 | | + burning calendar | 0.775 | | + 30-day dryness (this model) | 0.782 | | + buildings and roads in wildland (IGN BD TOPO) | 0.781 | Buildings and roads near vegetation add nothing once each commune's fire history is known, so they are not in the model. **Calibration** holds in the bands used by the map: commune-days predicted at 0.1–0.3% had a mean prediction of 0.167% and an observed rate of 0.169%; those at 0.3–1%, 0.38% predicted against 0.45% observed. Above 1% there are only 17 commune-days, too few to check. **Robustness** (model against FWI × commune history): - Without 2023: ROC AUC 0.787 against 0.761 (better in 16 of 18 years). - Fires of 1 ha or more: ROC AUC 0.780 against 0.720 (better in 16 of 19 years). **Location within a day is hard to improve.** None of the fixed rankings tested puts more than about 25% of fire starts in the day's 15 highest-ranked communes. This is what the rankings tested reach, not a proven ceiling. Larger models (CatBoost, the Timestral time-series model) were tested in the project too. They do no better and are less consistent from year to year. These results were corrected after an independent review of the code and claims on 9 November 2026 (inputs built from recent fire records were removed, two small leaks were closed, wording was corrected). See the "Update after an independent review" section of [`reports/phase1_results.md`](https://github.com/guillaumegay13/firepredict/blob/main/reports/phase1_results.md). ## 2026 retrospective test and live archive The inputs were chosen after looking at results across all years, so the scores above are somewhat optimistic. Two cleaner tests are running: - **Retrospective 2026 test, blind to the outcomes.** On 9 November 2026, before any 2026 fire record was published, the model was frozen and its forecasts for 1 January – 9 November 2026 were saved in `forecasts_2026.csv` (SHA-256 `2be6091ab25539dc47bb751822d2a9b2b48f21218f8238bf0be1e1653dc8c6d6`). They were all computed on that day, from the weather available then. This is a retrospective test, not a live trial. When BDIFF publishes the 2026 fires, the forecasts will be scored as they are. - **Live archive.** From 9 November 2026, each day's forecast from the frozen model is saved in the [`forecasts/`](https://github.com/guillaumegay13/firepredict/tree/main/forecasts) folder of the GitHub repository and committed the same day, so the git history dates it. ## Version 4 (folder `v4/`, current) `small_commune_v4`, frozen on 9 November 2026, is one logistic regression for the Var and the Bouches-du-Rhône. Inputs (`v4/metadata.json` lists them with weights and fill values): each commune's usual rate (positive commune-days per usable day, smoothed toward its department's rate by empirical Bayes), local FWI, 30-day rain minus evaporation, the burning periods, the date as a smooth yearly curve (by department), and each commune's seasonal pattern (its share of fire days in June–September, smoothed toward its department's share). The commune history used for forecasts is in `v4/commune_history.csv`. It was chosen from a ladder of variants evaluated with leave-one-year-out and forward-in-time tests. It has a lower log loss than v2/v3 in every test and corrects v3's clear calibration failures (Var summer overestimated 1.6× in the forward test; Bouches-du-Rhône winter 3.6×). Variants with recency weights or a sharper seasonal curve were not reliably better. **Against the FWI**, every-year test (each year hidden in both departments), positive commune-days caught, 95% ranges from resampling years: | Warning rule | Var: FWI alone / FWI × history → v4 | Bouches-du-Rhône: FWI alone / FWI × history → v4 | |---|---|---| | 15 communes every day | 85 / 222 → **237** | 169 / 602 → **632** | | 30 communes every day | 190 / 328 → **377** (+15%, +6% to +25%, vs history) | 323 / 818 → **834** | | ROC AUC | 0.718 / 0.757 → **0.784** | 0.781 / 0.858 → **0.869** | Against the FWI alone, v4 catches 2.8× (Var) and 3.7× (Bouches-du-Rhône) more positive commune-days at 15 warnings a day. Against FWI × commune history the gain is modest (+2% to +16% depending on the warning level and department; not significant in every case), and on its very top warnings it is no better. **Status.** Frozen as a research candidate for an outcome-blinded evaluation on 2026 labels. Historically it has a lower log loss than v2/v3, but it did not meet the original calibration acceptance criteria. An exploratory protocol, defined after inspecting results, found no clearly failing calibration groups; many groups remain inconclusive. Reliable subgroup calibration, superiority to the other variants, and operational usefulness have not been established. 2026 test: `v4/forecasts_2026.csv`, SHA-256 `1770b0b5e51aea65d39383043e3a7480d063574a1160d3eb52bfae00237b959e`; the scoring rule is fixed in `v4/metadata.json`. Daily v4 forecasts are archived in the GitHub repository under `forecasts/v4/`. ## Version 3 (folder `v3/`) `small_commune_v3`, frozen on 9 November 2026, is a single logistic regression trained on the Var and the Bouches-du-Rhône together (2,140 positive commune-days). It uses the five inputs above plus: - `rate_x_summer`: log usual rate × June–September. Busy communes concentrate their fires in summer more than quiet ones; without this term the model underestimated the Var's busiest communes in summer by half and overestimated Aix-en-Provence in winter. - `dept_13`: one level per department (1 for the Bouches-du-Rhône), so the chances are calibrated in each department. In a transfer test, rankings carried over between departments but probabilities did not. `v3/usual_rates.csv` has a `department` column; `v3/metadata.json` lists the inputs, weights and fill values. Every-year test, each year hidden in both departments at once (positive commune-days): | | Var (83) | Bouches-du-Rhône (13) | |---|---|---| | ROC AUC, v3 | 0.779 (v2: 0.782) | 0.864 (single-department model: 0.865) | | FWI × commune history | 0.757; v3 better in 17 / 19 years | 0.858; v3 better in 13 / 20 years | | 15 communes a day: caught | 219 (reference 220) | 630 (reference 605) | Calibration holds in both departments up to 1%; above 1% in the Var the model predicts 1.2% where 0.8% happened. The same limits apply as for v2 (exploratory results, approximate FWI, two neighbouring departments only). The 2026 test of v3 covers both departments: `v3/forecasts_2026.csv`, SHA-256 `783debd1cd6848fb0cd667ef2abc418a2221379c9698366e1e6ead8eae0a856c`. Daily v3 forecasts are archived in the GitHub repository under `forecasts/v3/`. ## Limitations - **Exploratory results.** The inputs and the 30-day window were chosen after looking at the same years used for evaluation, so p-values and ranges are optimistic. The ranges are conditional on the chosen model. - **Approximate FWI.** Computed from daily station values, not the official noon-based FWI or Météo-France's forecasts. - **One department.** The model is trained and tested on the Var only. It has not been checked on other departments. - **Label issues.** 2024 is excluded: BDIFF holds only 3 Var fires for 2024, against 50–380 in other years. In 2023 BDIFF started recording many more tiny fires, so only fires of 0.1 ha or more are counted. - **Commune-level locations only.** BDIFF gives the commune of each fire, not its position. - **Burning calendar.** The 2013 and 2025 rules are applied to all years. The 2006–2012 rules and the prefect's extra red days are not included. - **Past features use later knowledge.** Weather stations were chosen by their coverage over 2006–2026, and the leave-one-year-out test trains on years after the one it scores. - **Small chances.** Most predicted chances are below 1% per commune-day; they are useful to rank communes and days, not as yes/no forecasts. ## Data and credits The model was trained on the data below. This repository does not contain BDIFF or Météo-France raw data; the code in the GitHub repository downloads it from the official sites. `usual_rates.csv` holds yearly averages per commune derived from BDIFF. | Data | Provider | Licence | |---|---|---| | Fire records 2006–2025 (BDIFF) | Ministère de l'Agriculture, [bdiff.agriculture.gouv.fr](https://bdiff.agriculture.gouv.fr) | see below | | Daily weather, all Var stations | Météo-France, via [data.gouv.fr](https://www.data.gouv.fr/datasets/donnees-climatologiques-de-base-quotidiennes) | Licence Ouverte / Etalab 2.0 | | Burning calendar | Var prefectoral orders of 16 May 2013 and 2 November 2025 | public acts | | Commune boundaries (to match each commune to its nearest stations) | geo.api.gouv.fr (IGN, INSEE) | Licence Ouverte / Etalab 2.0 | The wider project (GitHub repository) also uses IGN BD TOPO, DDTM du Var burned-area outlines, Corine Land Cover 2018 and school holiday data; none of them is an input of this model. **BDIFF.** Source : Base de Données sur les Incendies de Forêts en France, [bdiff.agriculture.gouv.fr](https://bdiff.agriculture.gouv.fr). Droits de reproduction réservés et strictement limités. BDIFF data may be reused for personal, associative or professional purposes, unchanged, with this source cited, and may not be sold or rented. ## Licence Code and model under the MIT licence (see `LICENSE`). Data under the licences above. ## Citation ```bibtex @software{gay2026firepredict, author = {Gay, Guillaume}, title = {firepredict: daily wildfire-start risk for the communes of the Var, France, from open data}, year = {2026}, url = {https://github.com/guillaumegay13/firepredict}, note = {Model small\_commune\_v2, frozen 9 November 2026} } ``` ## Contact Guillaume Gay, guillaume.gay@protonmail.com. Feedback from fire services, researchers and local authorities is very welcome.