1

Reformulations

This commit is contained in:
Richard Dern 2025-11-26 17:22:57 +01:00
parent 8b26c0bf17
commit f4bdbe2c7f
13 changed files with 460 additions and 171 deletions

View File

@ -2,7 +2,8 @@
## Installation de l'environnement de base
Après avoir cloné le dépôt :
Après avoir cloné le dépôt, on commence par préparer un environnement Python isolé pour ne pas polluer le système global et pouvoir supprimer facilement tout ce qui concerne ce projet.
Lidée est de regrouper au même endroit le moteur [`Python`](https://www.python.org/), les bibliothèques scientifiques et le client pour la base de données [`InfluxDB`](https://www.influxdata.com/), qui stocke nos séries temporelles.
```shell
python3 -m venv .venv
@ -12,11 +13,11 @@ pip install -r requirements.txt
python -c "import pandas, influxdb_client, sklearn; print('OK')"
```
- On installe l'environnement virtuel de python
- On entre dans cet environnement
- On met à jour le gestionnaire de paquets pip
- On installe les dépendances définies dans `requirements.txt`
- On vérifie que les dépendances sont correctement installées
- `python3 -m venv .venv` crée un environnement virtuel Python local dans le dossier `.venv`, ce qui permet disoler les paquets de ce projet du reste du système (voir par exemple la documentation sur les environnements virtuels [`venv`](https://docs.python.org/fr/3/library/venv.html)).
- `source .venv/bin/activate` active cet environnement dans le shell courant ; tant quil est actif, la commande `python` pointera vers linterpréteur de `.venv` et non vers celui du système.
- `python -m pip install --upgrade pip` met à jour [`pip`](https://pip.pypa.io/), le gestionnaire de paquets de Python, afin déviter les bugs ou limitations des versions trop anciennes.
- `pip install -r requirements.txt` installe toutes les dépendances nécessaires au projet (notamment [`pandas`](https://pandas.pydata.org/), le client Python pour InfluxDB et [`scikit-learn`](https://scikit-learn.org/stable/) pour les modèles prédictifs) en suivant la liste déclarée dans `requirements.txt`.
- `python -c "import pandas, influxdb_client, sklearn; print('OK')"` vérifie que les principales bibliothèques simportent correctement ; si la commande affiche `OK`, lenvironnement de base est prêt à être utilisé.
## Configuration
@ -24,23 +25,26 @@ python -c "import pandas, influxdb_client, sklearn; print('OK')"
cp .env.example .env
```
On copie le fichier de configuration d'exemple, puis on l'ouvre pour l'adapter à notre cas.
On copie le fichier de configuration d'exemple, puis on l'ouvre pour l'adapter à notre cas. Ce fichier `.env` sera lu automatiquement par les scripts via [`python-dotenv`](https://github.com/theskumar/python-dotenv) (voir `meteo/config.py`), ce qui évite dexporter les variables à la main à chaque session.
- `INFLUXDB_URL` : URL de l'api du serveur InfluxDB2 (cela inclue probablement le port 8086)
- `INFLUXDB_TOKEN` : le jeton d'authentification à créer dans votre compte InfluxDB2
- `INFLUXDB_ORG` : l'organisation à laquelle le token est rattaché
- `INFLUXDB_BUCKET` : le nom du bucket dans lequel les données sont stockées
- `STATION_LATITUDE` : latitude GPS de la station météo
- `STATION_LONGITUDE` : longitude GPS de la station météo
- `STATION_ELEVATION` : altitude de la station météo
- `INFLUXDB_URL` : URL de l'API du serveur InfluxDB 2.x (incluant généralement le port 8086), par exemple `http://localhost:8086` ou ladresse de votre serveur ; cest le point dentrée HTTP/HTTPS vers votre base de données de séries temporelles (voir aussi lintroduction aux [bases de données de séries temporelles](https://fr.wikipedia.org/wiki/Base_de_donn%C3%A9es_de_s%C3%A9ries_temporelles)).
- `INFLUXDB_TOKEN` : jeton d'authentification généré dans linterface d[`InfluxDB 2.x`](https://docs.influxdata.com/influxdb/v2/), associé à un jeu de permissions (lecture/écriture) ; sans ce token, le client Python ne peut pas interroger le serveur.
- `INFLUXDB_ORG` : nom de l'organisation InfluxDB à laquelle le token est rattaché ; InfluxDB 2.x organise les ressources (utilisateurs, buckets, tokens) par organisation, il faut donc préciser celle que lon souhaite utiliser.
- `INFLUXDB_BUCKET` : nom du bucket (espace logique de stockage avec sa politique de rétention) dans lequel les données sont enregistrées ; cest ce bucket que les scripts interrogeront pour récupérer les mesures de la station.
- `STATION_LATITUDE` : latitude GPS de la station météo (en degrés décimaux), utilisée plus loin pour les calculs délévation solaire et pour enrichir les données avec des métadonnées astronomiques.
- `STATION_LONGITUDE` : longitude GPS de la station météo (en degrés décimaux), nécessaire pour les mêmes raisons que la latitude.
- `STATION_ELEVATION` : altitude de la station météo (en mètres au-dessus du niveau de la mer) ; cette information affine légèrement certains calculs physiques, mais reste optionnelle si laltitude est mal connue.
## Tests de l'environnement de travail
Avant dattaquer des analyses plus lourdes, on vérifie que la connexion au serveur InfluxDB fonctionne bien et que la configuration est cohérente.
Les scripts de test qui suivent nécrivent rien dans la base : ils se contentent deffectuer quelques requêtes simples pour valider laccès.
```shell
python "docs/01 - Installation, configuration et tests/scripts/test_influx_connection.py"
```
```output
```text
Configuration InfluxDB chargée :
URL : http://10.0.3.2:8086
Org : Dern
@ -57,13 +61,15 @@ Exemple de point :
value : humidity
```
Si vous obtenez un résultat similaire (URL affichée, ping OK, requête de test qui retourne quelques enregistrements), cest que le serveur InfluxDB est joignable, que le token est valide et que le bucket indiqué existe bien.
Ensuite, on peut demander à InfluxDB de nous détailler ce qu'il stocke :
```shell
python "docs/01 - Installation, configuration et tests/scripts/test_influx_schema.py"
```
```output
```text
Bucket InfluxDB : weather
Measurements disponibles :
@ -115,13 +121,16 @@ Champs pour measurement « °C » :
- value (type: unknown)
```
Ce deuxième script interroge le schéma du bucket : il liste les _measurements_ (grandeurs physiques comme `%`, `°C`, `km/h`, etc.), ainsi que les champs associés à chacun.
Dans InfluxDB, un _measurement_ correspond en gros à un type de mesure (par exemple une unité), et les _fields_ contiennent les valeurs numériques que lon exploitera plus tard ; les metadata (comme `entity_id`) sont stockées sous forme de _tags_ (voir la documentation sur le [modèle de données InfluxDB](https://docs.influxdata.com/influxdb/v2/reference/key-concepts/data-elements/)).
Mais pour obtenir les données dont on a besoin, il faut aussi connaitre les entités manipulées par Influx :
```shell
python "docs/01 - Installation, configuration et tests/scripts/test_influx_entities.py"
```
```output
```text
Bucket InfluxDB : weather
Measurement « % »
@ -202,6 +211,9 @@ Measurement « °C »
- station_meteo_bresser_exterieur_temperature
```
Ces informations combinées se retrouvent dans le fichier `meteo/station_config.py` et dans `meteo/variables.py`.
Ce dernier script fait le lien avec la source des données : il dresse la liste des clés de tags et des `entity_id` possibles pour chaque _measurement_.
Ces identifiants correspondent aux entités exposées par votre système domotique (par exemple [`Home Assistant`](https://www.home-assistant.io/)), et permettent de distinguer clairement lhumidité extérieure, la pression, la vitesse du vent, etc.
Ces informations combinées se retrouvent dans le fichier `meteo/station_config.py` et dans `meteo/variables.py` : cest là que lon fixe, une fois pour toutes, quelles entités InfluxDB seront considérées comme « température extérieure », « pluie », « vent », et sous quels noms elles seront manipulées dans la suite de létude.
On aurait pu se passer de ces scripts pour déterminer la structure des données stockées dans Influx, mais ils évitent de se reposer sur des intuitions : ici, on demande à Influx de nous donner les informations dont on va avoir besoin au lieu de les deviner.

View File

@ -1,6 +1,7 @@
# Préparation des données
Cette étape regroupe l'export initial depuis InfluxDB ainsi que les scripts d'ajustement nécessaires pour obtenir un dataset minuté propre.
Cette étape regroupe l'export initial depuis InfluxDB ainsi que les scripts d'ajustement nécessaires pour obtenir un dataset minuté propre, cestàdire une [série temporelle](https://fr.wikipedia.org/wiki/S%C3%A9rie_temporelle) où chaque minute possède une observation complète pour toutes les variables utiles.
Lobjectif est de passer dun format brut, pensé pour la domotique temps réel, à un format tabulaire lisible par les bibliothèques danalyse comme `pandas`.
## Export des données
@ -8,10 +9,13 @@ Cette étape regroupe l'export initial depuis InfluxDB ainsi que les scripts d'a
python "docs/02 - Préparation des données/scripts/export_station_data.py"
```
Ce script se connecte au serveur InfluxDB configuré au chapitre précédent, interroge les mesures de la station sur une fenêtre récente (par défaut les sept derniers jours) et les exporte dans un fichier CSV `data/weather_raw_7d.csv`.
On quitte ainsi la base de données de séries temporelles pour un format beaucoup plus simple à manipuler avec `pandas`, tout en conservant lhorodatage précis.
La sortie est assez longue, et inclut un certain nombre d'avertissements qui peuvent être ignorés.
L'important est que le script se termine sur :
```output
```text
✔ Export terminé : /Users/richard/Documents/donnees_meteo/data/weather_raw_7d.csv
```
@ -25,18 +29,24 @@ Vérifiez que le fichier est bien créé et qu'il contient des données.
python "docs/02 - Préparation des données/scripts/export_station_data_full.py"
```
Au lieu de télécharger les données des 7 derniers jours, l'ensemble des données stockées sur le serveur pour ce bucket seront téléchargées, ce qui, selon la granularité et l'ancienneté des données peut prendre un certain temps et occuper un espace disque conséquent.
Au lieu de télécharger les données des sept derniers jours, l'ensemble des données stockées sur le serveur pour ce _bucket_ seront téléchargées, ce qui, selon la granularité et l'ancienneté des données, peut prendre un certain temps et occuper un espace disque conséquent.
Mon fichier complet contient plus d'un million d'enregistrements et pèse 70 Mo.
En pratique, il est souvent plus confortable de développer et tester les scripts suivants sur quelques jours de données (fichier `weather_raw_7d.csv`), puis de relancer le pipeline complet sur lhistorique complet une fois les étapes stabilisées.
Les deux scripts sappuient sur lAPI HTTP d[InfluxDB 2.x](https://docs.influxdata.com/influxdb/v2/) et son langage de requête Flux (voir la documentation dédiée à [Flux](https://docs.influxdata.com/flux/v0.x/)).
## Ajustements
Le CSV exporté reste très proche du format de stockage utilisé par la domotique : chaque capteur envoie une mesure à des instants légèrement différents, et InfluxDB stocke donc plusieurs lignes par seconde, chacune ne contenant quune seule variable renseignée.
Pour lanalyse statistique, on cherche au contraire à obtenir une table où chaque horodatage regroupe toutes les variables de la station sur une grille temporelle régulière.
Le fichier peut être rapidement inspecté avec la commande `head` :
```shell
head data/weather_raw_full.csv
```
```output
```text
time,temperature,humidity,pressure,illuminance,wind_speed,wind_direction,rain_rate
2025-03-10 09:35:23.156646+00:00,,,996.95,,,,
2025-03-10 09:35:23.158538+00:00,10.6,,,,,,
@ -49,7 +59,8 @@ time,temperature,humidity,pressure,illuminance,wind_speed,wind_direction,rain_ra
2025-03-10 09:36:22.640356+00:00,,,,,,306.0,
```
On peut voir que HomeAssistant écrit une nouvelle entrée pour chaque capteur, alors qu'on aurait pu s'attendre à une ligne unique pour l'ensemble des capteurs.
On peut voir que [Home Assistant](https://www.home-assistant.io/) écrit une nouvelle entrée pour chaque capteur, alors qu'on aurait pu s'attendre à une ligne unique pour l'ensemble des capteurs.
Cest un format très pratique pour la collecte temps réel, mais moins confortable pour lanalyse : il faut dabord tout remettre en forme.
Le script suivant s'occupe de regrouper les données de capteurs dont l'enregistrement est proche :
@ -57,7 +68,7 @@ Le script suivant s'occupe de regrouper les données de capteurs dont l'enregist
python "docs/02 - Préparation des données/scripts/format_raw_csv.py"
```
```output
```text
Fichier brut chargé : data/weather_raw_full.csv
Lignes : 1570931, colonnes : ['temperature', 'humidity', 'pressure', 'illuminance', 'wind_speed', 'wind_direction', 'rain_rate']
Type d'index : <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
@ -66,12 +77,15 @@ Après combinaison (1s) : 630171 lignes
```
Un nouveau document CSV intermédiaire est donc créé.
On voit que lon passe denviron 1,57 million de lignes à 630 000 lignes « combinées » : le script regroupe les mesures de tous les capteurs tombant dans la même seconde, en utilisant lhorodatage comme clé.
Lindex de type `DatetimeIndex` indiqué dans la sortie est laxe temporel standard de `pandas` (voir la documentation de [`pandas.DatetimeIndex`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DatetimeIndex.html)).
Cette étape correspond à une première forme de rééchantillonnage temporel, classique lorsquon prépare une série pour lanalyse.
```shell
head data/weather_formatted_1s.csv
```
```output
```text
time,temperature,humidity,pressure,illuminance,wind_speed,wind_direction,rain_rate
2025-03-10 09:35:23+00:00,10.6,83.0,996.95,,7.4,256.0,0.0
2025-03-10 09:35:41+00:00,,,,20551.2,,,
@ -84,7 +98,8 @@ time,temperature,humidity,pressure,illuminance,wind_speed,wind_direction,rain_ra
2025-03-10 09:40:22+00:00,,,,19720.8,10.0,209.0,
```
Il reste des cellules vides : en effet, HA n'enregistre pas la valeur d'un capteur si elle n'a pas changé depuis la dernière fois.
Il reste des cellules vides : en effet, Home Assistant n'enregistre pas la valeur d'un capteur si elle n'a pas changé depuis la dernière fois.
On se retrouve donc avec une série temporelle à pas régulier (1 s), mais encore parsemée de trous.
On fait donc :
@ -92,20 +107,22 @@ On fait donc :
python "docs/02 - Préparation des données/scripts/fill_formatted_1s.py"
```
```output
```text
Fichier 1s formaté chargé : data/weather_formatted_1s.csv
Lignes : 630171, colonnes : ['temperature', 'humidity', 'pressure', 'illuminance', 'wind_speed', 'wind_direction', 'rain_rate']
Après propagation des dernières valeurs connues : 630171 lignes
✔ Fichier 1s 'complet' écrit dans : /Users/richard/Documents/donnees_meteo/data/weather_filled_1s.csv
```
Cette seconde passe applique un remplissage par propagation de la dernière valeur connue (_forward fill_ ou `ffill` dans `pandas`) : tant quun capteur ne publie pas de nouvelle mesure, on considère que la précédente reste valable. Cest une hypothèse raisonnable pour des variables relativement lisses comme la température ou la pression, moins pour des phénomènes très brusques ; lobjectif ici est surtout dobtenir un _dataset_ sans [valeurs manquantes](https://fr.wikipedia.org/wiki/Donn%C3%A9es_manquantes), ce qui simplifie grandement les analyses et les modèles dans les chapitres suivants.
## Enrichissements (saisons et position du soleil)
Une fois les données nettoyées, on peut les enrichir avec des métadonnées météorologiques simples :
Une fois les données nettoyées, on peut les enrichir avec des métadonnées météorologiques simples, qui aideront ensuite à interpréter les graphiques et à construire des modèles plus pertinents :
- regrouper les points par minute,
- ajouter la saison correspondant à chaque observation (en fonction de l'hémisphère),
- calculer la hauteur du soleil si la latitude/longitude de la station sont configurées.
- regrouper les points par minute, pour lisser légèrement le bruit tout en restant réactif ; ce pas de 60 secondes est un bon compromis entre fidélité au signal brut et taille raisonnable du _dataset_ ;
- ajouter la saison correspondant à chaque observation (en fonction de l'hémisphère), ce qui permet de comparer facilement les comportements printemps/été/automne/hiver et de relier nos courbes aux notions classiques de [saisons](https://fr.wikipedia.org/wiki/Saison) en météorologie ;
- calculer la hauteur du soleil si la latitude/longitude de la station sont configurées, afin de disposer dune estimation de lélévation solaire audessus de lhorizon (jour/nuit, midi solaire, etc.) en sappuyant sur la bibliothèque dastronomie [`astral`](https://astral.readthedocs.io/).
Ces opérations sont réalisées par :
@ -113,26 +130,34 @@ Ces opérations sont réalisées par :
python "docs/02 - Préparation des données/scripts/make_minutely_dataset.py"
```
Le script produit `data/weather_minutely.csv`. Pensez à définir `STATION_LATITUDE`, `STATION_LONGITUDE` et `STATION_ELEVATION` dans votre `.env` pour permettre le calcul de la position du soleil ; sinon, seule la colonne `season` sera ajoutée.
Le script produit `data/weather_minutely.csv`. Chaque ligne de ce fichier correspond à une minute, avec toutes les variables météo alignées (température, humidité, pression, vent, pluie, etc.) et, si les coordonnées de la station sont connues, des colonnes supplémentaires comme la saison et lélévation du soleil.
> Pensez à définir `STATION_LATITUDE`, `STATION_LONGITUDE` et `STATION_ELEVATION` dans votre `.env` pour permettre le calcul de la position du soleil ; sinon, seule la colonne `season` sera ajoutée.
Ce fichier minuté est le jeu de données de référence utilisé dans la majorité des chapitres suivants.
## Pipeline simplifié
Un script tout simple permet de faire automatiquement tout ce qu'on vient de voir.
Un script tout simple permet de faire automatiquement tout ce qu'on vient de voir, dans le bon ordre, sans avoir à lancer chaque étape à la main.
Il supprime **tous** les fichiers CSV existants : il faudra donc relancer la génération des images dans les étapes suivantes pour qu'elles intègrent les nouvelles données.
```shell
python -m scripts.refresh_data_pipeline
```
Ce module Python orchestre lexport depuis InfluxDB, la mise en forme à 1 seconde, le remplissage des valeurs manquantes puis la construction du dataset minuté.
Le fait de repartir de zéro à chaque exécution garantit que `weather_minutely.csv` reflète bien létat actuel de la base, au prix dun temps de calcul un peu plus long.
## Vérification des données
On peut s'assurer que plus aucune information n'est manquante :
Avant dexplorer les graphiques ou de lancer des modèles, on vérifie que le dataset minuté est cohérent : pas de valeurs manquantes, des ordres de grandeur plausibles, et une grille temporelle effectivement régulière.
On peut dabord s'assurer que plus aucune information n'est manquante :
```shell
python "docs/02 - Préparation des données/scripts/check_missing_values.py"
```
```output
```text
Dataset chargé : data/weather_minutely.csv
Lignes : 321881
Colonnes : ['temperature', 'humidity', 'pressure', 'illuminance', 'wind_speed', 'wind_direction', 'rain_rate']
@ -156,13 +181,15 @@ Valeurs manquantes par colonne :
✔ Aucune valeur manquante dans le dataset minuté.
```
Ce premier contrôle confirme que toutes les lignes de `weather_minutely.csv` sont complètes : aucune cellule nest manquante, ce qui évitera bien des subtilités dans les analyses ultérieures.
Le script suivant nous permet de vérifier rapidement si des problèmes majeurs peuvent être découverts :
```shell
python "docs/02 - Préparation des données/scripts/describe_minutely_dataset.py"
```
```output
```text
Dataset minuté chargé : data/weather_minutely.csv
Lignes : 321881
Colonnes : ['temperature', 'humidity', 'pressure', 'illuminance', 'wind_speed', 'wind_direction', 'rain_rate'] Période : 2025-03-10 09:35:00+00:00 → 2025-11-17 00:41:00+00:00
@ -216,13 +243,17 @@ Name: count, dtype: int64
Nombre d'intervalles ≠ 60s : 17589
```
Ces écarts peuvent être identifiés avec le script suivant :
Ce deuxième script fournit un aperçu statistique classique (fonction `describe()` de `pandas`) et quelques min/max datés pour chaque variable : on y vérifie que les valeurs restent plausibles (par exemple pas de température à +80 °C ni de vent négatif) et que les extrêmes correspondent à des dates réalistes.
La section sur les différences dintervalle permet déjà de repérer que certains pas ne sont pas strictement de 60 secondes, ce qui est courant sur des données issues de capteurs, mais quil faut garder en tête pour la suite.
Ce type de contrôle sinscrit dans une démarche d[analyse exploratoire de données](https://fr.wikipedia.org/wiki/Analyse_exploratoire_de_donn%C3%A9es).
Ces écarts peuvent être identifiés plus finement avec le script suivant :
```shell
python "docs/02 - Préparation des données/scripts/list_time_gaps.py"
```
```
```text
Dataset minuté chargé : data/weather_minutely.csv
Lignes : 321881
@ -243,8 +274,9 @@ Top 10 des gaps les plus longs :
- De 2025-06-21 17:25:00+00:00 à 2025-06-21 18:10:00+00:00 (durée: 0 days 00:45:00, manquants: 44, de 2025-06-21 17:26:00+00:00 à 2025-06-21 18:09:00+00:00)
```
Ces trous dans les données peuvent correspondre à des pannes de connexion entre la station et mon réseau, un redémarrage de mon serveur (physique ou logiciel), au redémarrage de la box ou du point d'accès sans-fil, etc.
Ils peuvent aussi correspondre aux modifications opérées dans les scripts précédents.
Ce troisième script ne corrige rien : il se contente de lister précisément les intervalles dans lesquels des minutes manquent dans la série, ainsi que leur durée.
Ces trous dans les données peuvent correspondre à des pannes de connexion entre la station et mon réseau, un redémarrage de mon serveur (physique ou logiciel), au redémarrage de la box ou du point d'accès sansfil, etc.
Mais ils peuvent également correspondre aux modifications opérées dans les scripts précédents !
Ces scripts sont intéressants parce qu'ils mettent en évidence des facteurs indirects, contribuant à la qualité des données soumise.
On peut prendre toutes les précautions, on peut avoir l'intuition d'avoir tout géré, et se rassurer parce qu'on utilise des outils fiables, mais il existera toujours des manques dans les données.

View File

@ -1,13 +1,22 @@
# Premiers graphiques
On peut désormais tracer nos premiers graphiques simples et bruts.
S'ils ne sont pas très instructifs par rapport à ce que nous fournissent Home Assistant et InfluxDB, ils nous permettent au moins de nous assurer que tout fonctionne, et que les données semblent cohérentes.
Les fichiers CSV correspondant à chaque figure sont conservés dans `data/` dans ce dossier.
On peut désormais tracer nos premiers graphiques simples et bruts à partir du dataset minuté construit au chapitre précédent.
S'ils ne sont pas encore très instructifs par rapport à ce que nous fournissent déjà Home Assistant et InfluxDB, ils nous permettent au moins de nous assurer que tout fonctionne, que les données semblent cohérentes, et que la chaîne « export → préparation → visualisation » tient la route.
Les scripts de ce chapitre sappuient sur [`pandas`](https://pandas.pydata.org/) et sur la bibliothèque de visualisation [`Matplotlib`](https://matplotlib.org/) (via le module `meteo.plots`) pour produire des graphiques standards : séries temporelles, _heatmaps_, calendriers.
Les fichiers CSV correspondant à chaque figure sont conservés dans le sous-dossier `data/` de ce chapitre, ce qui permet de réutiliser directement les séries pré-agrégées si besoin.
```shell
python "docs/03 - Premiers graphiques/scripts/plot_basic_variables.py"
```
Ce script lit `data/weather_minutely.csv`, sélectionne éventuellement une fenêtre temporelle (par exemple les derniers jours si lon utilise loption `--days`) puis choisit une fréquence dagrégation adaptée pour ne pas saturer le graphique en points.
Pour chaque variable (température, pression, humidité, pluie, vent, illuminance, élévation du soleil), il applique un style par défaut : courbe continue pour les variables lisses, diagramme en barres pour la pluie, nuage de points pour la direction du vent, etc.
Lidée est dobtenir une première vue densemble de la [série temporelle](https://fr.wikipedia.org/wiki/S%C3%A9rie_temporelle) de chaque variable, sans prise de tête :
- vérifier le cycle quotidien de la température et de lélévation solaire ;
- repérer les paliers ou dérives de capteurs (pression trop plate, humidité coincée, etc.) ;
- confirmer que les unités, ranges et horodatages sont plausibles.
![](figures/temperature_overview.png)
![](figures/pressure_overview.png)
@ -30,6 +39,14 @@ python "docs/03 - Premiers graphiques/scripts/plot_basic_variables.py"
python "docs/03 - Premiers graphiques/scripts/plot_calendar_overview.py"
```
Le second script propose une vue complémentaire sous forme de _calendrier thermique_ (une [carte de chaleur](https://fr.wikipedia.org/wiki/Carte_de_chaleur) disposée en jours et mois).
À partir du même dataset minuté, il calcule des moyennes quotidiennes (température, humidité, pression, illuminance, vent) ou des cumuls quotidiens (pluie), puis remplit une matrice « mois x jours » pour lannée la plus récente disponible.
Ces vues servent surtout à :
- repérer rapidement les saisons, épisodes pluvieux ou périodes de vent soutenu ;
- détecter des trous dans la série (jours entièrement manquants) ;
- avoir un aperçu global de lannée sans zoomer/dézoomer en permanence sur une longue série temporelle.
![](figures/calendar/calendar_temperature_2025.png)
![](figures/calendar/calendar_pressure_2025.png)

View File

@ -1,11 +1,22 @@
# Corrélations binaires
Lobjectif de ce chapitre est dexplorer les relations entre variables deux à deux : dabord visuellement (superposition de séries temporelles, comme ci-dessous, et [nuages de points](https://fr.wikipedia.org/wiki/Nuage_de_points), comme dans le chapitre suivant), puis numériquement via des coefficients de [corrélation](<https://fr.wikipedia.org/wiki/Corr%C3%A9lation_(statistiques)>).
On reste volontairement dans un cadre simple : une variable « primaire » et une variable « associée », à la fois dans le temps et dans les matrices de corrélation.
## Superpositions simples
```shell
python "docs/04 - Corrélations binaires/scripts/plot_pairwise_time_series.py"
```
Ce script parcourt toutes les paires de variables disponibles et produit, pour chacune, un graphique superposant les deux séries sur le même axe temporel (ou sur deux axes verticaux si nécessaire).
Les données sont ré-échantillonnées pour limiter le nombre de points et lisser légèrement le bruit, en utilisant les mêmes mécanismes que dans le chapitre 3.
Ces superpositions servent surtout à repérer des coévolutions évidentes (par exemple humidité qui baisse quand la température monte) ou, au contraire, des paires qui semblent indépendantes à lœil nu.
### Température
Toutes les figures de cette section ont pour variable « primaire » la température ; lautre variable change dun graphique à lautre.
![](figures/pairwise_timeseries/timeseries_temperature_vs_humidity.png)
![](figures/pairwise_timeseries/timeseries_temperature_vs_pressure.png)
@ -20,6 +31,10 @@ python "docs/04 - Corrélations binaires/scripts/plot_pairwise_time_series.py"
![](figures/pairwise_timeseries/timeseries_temperature_vs_sun_elevation.png)
### Humidité relative
Ici, on fixe lhumidité comme variable principale et on observe comment elle évolue en parallèle des autres variables.
![](figures/pairwise_timeseries/timeseries_humidity_vs_pressure.png)
![](figures/pairwise_timeseries/timeseries_humidity_vs_rain_rate.png)
@ -32,6 +47,10 @@ python "docs/04 - Corrélations binaires/scripts/plot_pairwise_time_series.py"
![](figures/pairwise_timeseries/timeseries_humidity_vs_sun_elevation.png)
### Pression
Dans ces vues, on suit la pression atmosphérique et on la compare aux autres champs mesurés.
![](figures/pairwise_timeseries/timeseries_pressure_vs_rain_rate.png)
![](figures/pairwise_timeseries/timeseries_pressure_vs_illuminance.png)
@ -42,18 +61,30 @@ python "docs/04 - Corrélations binaires/scripts/plot_pairwise_time_series.py"
![](figures/pairwise_timeseries/timeseries_pressure_vs_sun_elevation.png)
### Pluviométrie
On regarde ici comment les épisodes de pluie (taux de précipitation) se positionnent par rapport au vent et à la hauteur du soleil.
![](figures/pairwise_timeseries/timeseries_rain_rate_vs_wind_speed.png)
![](figures/pairwise_timeseries/timeseries_rain_rate_vs_wind_direction.png)
![](figures/pairwise_timeseries/timeseries_rain_rate_vs_sun_elevation.png)
### Luminance
Ces superpositions éclairent les liens entre lumière, vent et position du soleil.
![](figures/pairwise_timeseries/timeseries_illuminance_vs_wind_speed.png)
![](figures/pairwise_timeseries/timeseries_illuminance_vs_wind_direction.png)
![](figures/pairwise_timeseries/timeseries_illuminance_vs_sun_elevation.png)
### Vent (vitesse / direction)
Enfin, on se concentre sur le vent : dabord sa vitesse en lien avec lélévation solaire, puis la direction comparée à cette même référence.
![](figures/pairwise_timeseries/timeseries_wind_speed_vs_wind_direction.png)
![](figures/pairwise_timeseries/timeseries_wind_speed_vs_sun_elevation.png)
@ -62,8 +93,12 @@ python "docs/04 - Corrélations binaires/scripts/plot_pairwise_time_series.py"
## Matrices de corrélation (instantané, signé)
Le calcul des coefficients de Pearson et de Spearman peut nous donner une indication numérique des corrélations entre les différentes variables.
Cette information peut nous aiguiller sur des relations spécifiques (ou au contraire, nous induire en erreur...).
Le calcul des coefficients de Pearson et de Spearman peut nous donner une indication numérique de la force et du signe des corrélations entre les différentes variables.
On passe ainsi du visuel (superpositions, nuages de points) à un résumé compact des covariations, même si cela ne capture que des dépendances linéaires ou monotones simples.
On utilise ici :
- le coefficient de corrélation linéaire de Pearson (voir [corrélation linéaire](https://fr.wikipedia.org/wiki/Corr%C3%A9lation_lin%C3%A9aire)) pour mesurer à quel point deux variables varient ensemble de manière approximativement linéaire ;
- le coefficient de Spearman (voir [corrélation de Spearman](https://fr.wikipedia.org/wiki/Corr%C3%A9lation_de_Spearman)) pour capturer des relations monotones (croissantes ou décroissantes), même si elles ne sont pas parfaitement linéaires.
```shell
python "docs/04 - Corrélations binaires/scripts/plot_correlation_heatmap.py" --transform=identity --upper-only
@ -73,5 +108,5 @@ python "docs/04 - Corrélations binaires/scripts/plot_correlation_heatmap.py" --
![](figures/correlation_heatmap_spearman.png)
Le signe et l'intensité des coefficients montrent à quel point deux variables bougent ensemble au même instant (co-mouvement linéaire pour Pearson, monotone pour Spearman).
Le signe et l'intensité des coefficients montrent à quel point deux variables bougent ensemble au même instant (comouvement linéaire pour Pearson, monotone pour Spearman).
Cette matrice sert donc surtout de carte globale : repérer rapidement les couples très corrélés ou indiquer un lien physique évident, mettre en alerte des variables à forte corrélation qui pourraient masquer d'autres effets (saisonnalité, cycle jour/nuit), et choisir quelles paires méritent qu'on teste des décalages temporels ou des relations non linéaires dans la suite.

View File

@ -381,22 +381,22 @@ Les régimes diurnes/nocturnes modulent [la direction](https://en.wikipedia.org/
Conclusion : le pattern jour/nuit est plausible ; sans mesure régionale, on ne peut isoler précisément le mécanisme.
## Données manquantes
## Synthèse des données manquantes
| Relation | Variable | Comment se la procurer |
| -------- | -------- | ---------------------- |
| Température ↔ Humidité relative | Eau précipitable intégrée (PWV) | Reanalyses ERA5/AROME via [API Copernicus CDS](https://cds.climate.copernicus.eu/api-how-to), radiosondages proches, station GNSS locale pour PWV. |
| Température ↔ Luminance / Humidité ↔ Luminance / Élévation solaire ↔ Humidité | Couverture/épaisseur nuageuse | Meteosat via [EUMETSAT Data Store](https://data.eumetsat.int/), METAR/TAF via [NOAA ADDS API](https://aviationweather.gov/data/api/), reanalyses fraction nuageuse via CDS API, caméra ciel locale. |
| Température ↔ Luminance / Élévation solaire ↔ Température | Humidité du sol | Capteur sol, reanalyses ERA5-Land via [CDS](https://cds.climate.copernicus.eu), SAFRAN Météo-France ([données publiques](https://donneespubliques.meteofrance.fr/) ou [data.gouv.fr](https://www.data.gouv.fr/)), API AROME WCS si accessible. |
| Température ↔ Luminance | Albédo du sol | [Corine](https://land.copernicus.eu/pan-european/corine-land-cover) ou [OSM](https://www.openstreetmap.org/) en open data ; NDVI Sentinel/Landsat (ex. [Sentinel Hub](https://www.sentinel-hub.com/)) pour dériver lalbédo. |
| Vent (vitesse/direction) ↔ Température/Pluie | Champ de pression régional / gradient horizontal | ERA5 via CDS, GFS via [NOAA NOMADS](https://nomads.ncep.noaa.gov/), cartes synoptiques publiques, stations baro proches ([data.gouv.fr](https://www.data.gouv.fr/)). |
| Humidité ↔ Pluie / Pluie ↔ Élévation solaire | Profils verticaux (T/HR/vent) | Radiosondages [NOAA IGRA](https://www.ncei.noaa.gov/products/weather-balloon/integrated-global-radiosonde-archive), reanalyses horaires ERA5/AROME via CDS, profils AROME HD (API Météo-France si éligible). |
| Humidité ↔ Pluie / Pluie ↔ Élévation solaire | Indices de stabilité (CAPE/CIN) | Calculés depuis ERA5/AROME via CDS ou sondes ; outils [MetPy](https://unidata.github.io/MetPy/latest/) ou [wgrib2](https://www.cpc.ncep.noaa.gov/products/wesley/wgrib2/). |
| Direction ↔ Humidité / Pluie | Trajectoires des masses dair (back-trajectoires) | [HYSPLIT NOAA READY](https://www.ready.noaa.gov/HYSPLIT.php), services NOAA en ligne, scripts [PySPLIT](https://pysplit.readthedocs.io/). |
| Luminance ↔ Élévation solaire | Opacité atmosphérique / aérosols | [AERONET API](https://aeronet.gsfc.nasa.gov/new_web/API/index.html), [CAMS](https://ads.atmosphere.copernicus.eu/), PM locaux via [OpenAQ](https://openaq.org/), MODIS AOD via [NASA LAADS](https://ladsweb.modaps.eosdis.nasa.gov/). |
| Luminance ↔ Vent / Température | Flux radiatif net (SW/LW) | Pyranomètre/pyrgéomètre locaux, reanalyses de surface ERA5 via CDS, stations agricoles/météo proches en open data si disponibles. |
| Vent ↔ Température/Pluie | Rugosité / traînée de surface | Corine/OSM, LIDAR [IGN RGE Alti](https://geoservices.ign.fr/rgealti), survey terrain pour estimer z0. |
| Toutes relations | Données hivernales complètes | Continuer la collecte jusquà fin dhiver ; combler avec reanalyses ERA5 via CDS API pour les périodes manquantes. |
| ----------------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Température ↔ Humidité relative | Eau précipitable intégrée (PWV) | Réanalyses ERA5/AROME via [API Copernicus CDS](https://cds.climate.copernicus.eu/api-how-to), radiosondages Météo-France (accessibles via [donneespubliques.meteofrance.fr](https://donneespubliques.meteofrance.fr/) ou intégrés dans [NOAA IGRA](https://www.ncei.noaa.gov/products/weather-balloon/integrated-global-radiosonde-archive)), réseau GNSS permanent (ex. [RGP de lIGN](https://geodesie.ign.fr/index.php?page=rgp)) ou station GNSS locale pour le PWV. |
| Température ↔ Luminance / Humidité ↔ Luminance / Élévation solaire ↔ Humidité | Couverture/épaisseur nuageuse | Imagerie Meteosat via [EUMETSAT Data Store](https://data.eumetsat.int/) ou produits dérivés de Météo-France, bulletins METAR/SYNOP (nébulosité) via [donneespubliques.meteofrance.fr](https://donneespubliques.meteofrance.fr/) ou [data.gouv.fr](https://www.data.gouv.fr/), fraction nuageuse des réanalyses ERA5/AROME via CDS API, caméra ciel locale. |
| Température ↔ Luminance / Élévation solaire ↔ Température | Humidité du sol | Capteur sol in situ, réanalyses ERA5-Land via [CDS](https://cds.climate.copernicus.eu), analyse SAFRAN Météo-France ([données publiques](https://donneespubliques.meteofrance.fr/) ou [data.gouv.fr](https://www.data.gouv.fr/)), champs AROME/ARPEGE de surface si accessibles (API ou jeux climatologiques). |
| Température ↔ Luminance | Albédo du sol | Cartes doccupation du sol [Corine](https://land.copernicus.eu/pan-european/corine-land-cover) ou [OSM](https://www.openstreetmap.org/) en open data ; NDVI Sentinel/Landsat (ex. [Sentinel Hub](https://www.sentinel-hub.com/)) pour dériver lalbédo, complété au besoin par les produits dalbédo Copernicus. |
| Vent (vitesse/direction) ↔ Température/Pluie | Champ de pression régional / gradient horizontal | Réanalyses ERA5 via CDS, champs de pression AROME/ARPEGE Météo-France (données publiques ou produits dérivés), GFS via [NOAA NOMADS](https://nomads.ncep.noaa.gov/), cartes synoptiques publiques, stations baro proches (réseau Météo-France ou stations amateurs sur [data.gouv.fr](https://www.data.gouv.fr/)). |
| Humidité ↔ Pluie / Pluie ↔ Élévation solaire | Profils verticaux (T/HR/vent) | Radiosondages Météo-France (Trappes, Nîmes, etc.) via [donneespubliques.meteofrance.fr](https://donneespubliques.meteofrance.fr/) ou archives [NOAA IGRA](https://www.ncei.noaa.gov/products/weather-balloon/integrated-global-radiosonde-archive), réanalyses horaires ERA5/AROME via CDS, profils AROME HD si accessibles. |
| Humidité ↔ Pluie / Pluie ↔ Élévation solaire | Indices de stabilité (CAPE/CIN) | Calculés depuis ERA5/AROME via CDS ou à partir de radiosondages ; outils [MetPy](https://unidata.github.io/MetPy/latest/) ou [wgrib2](https://www.cpc.ncep.noaa.gov/products/wesley/wgrib2/), certains produits dérivés disponibles dans les jeux climatologiques Météo-France ou via services universitaires français. |
| Direction ↔ Humidité / Pluie | Trajectoires des masses dair (back-trajectoires) | [HYSPLIT NOAA READY](https://www.ready.noaa.gov/HYSPLIT.php) (forçage GFS ou réanalyses globales), scripts [PySPLIT](https://pysplit.readthedocs.io/), et, pour la France, champs de vent issus dAROME/ARPEGE (Météo-France ou CDS) pour forcer des trajectoires régionales plus fines. |
| Luminance ↔ Élévation solaire | Opacité atmosphérique / aérosols | Réseau photomètres [AERONET API](https://aeronet.gsfc.nasa.gov/new_web/API/index.html) (plusieurs sites en France), produits [CAMS](https://ads.atmosphere.copernicus.eu/) sur les aérosols, mesures de particules fines via [OpenAQ](https://openaq.org/) et réseaux de qualité de lair français, MODIS AOD via [NASA LAADS](https://ladsweb.modaps.eosdis.nasa.gov/). |
| Luminance ↔ Vent / Température | Flux radiatif net (SW/LW) | Pyranomètre/pyrgéomètre locaux, réanalyses de surface ERA5 via CDS, bilans radiatifs de surface dans les produits Météo-France (SAFRAN/AROME Surface) si disponibles, stations agricoles ou réseaux météo/agro français en open data. |
| Vent ↔ Température/Pluie | Rugosité / traînée de surface | Cartes doccupation du sol Corine/OSM, modèles numériques de terrain et LIDAR [IGN RGE Alti](https://geoservices.ign.fr/rgealti), inventaires locaux (haies, forêts, bâtiments) pour estimer la rugosité z0 autour de la station. |
| Toutes relations | Données hivernales complètes | Continuer la collecte jusquà fin dhiver ; compléter les manques avec les réanalyses ERA5/ERA5-Land via CDS API et, si besoin, les produits climatologiques de Météo-France pour les périodes plus anciennes. |
## Conclusion

View File

@ -2,42 +2,57 @@
## Hexbin colorés
Les nuages de points tri-variés saturent vite : on regroupe ici les points sur une grille hexagonale et on colore chaque case par une statistique de la 3ᵉ variable (max/médiane/moyenne selon les besoins). Cela limite le bruit des outliers et met en évidence les régimes dominants plutôt que les valeurs isolées. Les scénarios sont décrits dans `meteo/correlation_presets.py` et exécutés via un helper générique (`meteo.plots.hexbin`) pour rester réutilisables ailleurs dans le dépôt.
Les nuages de points tri-variés saturent vite : on regroupe ici les points sur une grille hexagonale et on colore chaque case par une statistique de la 3ᵉ variable (max/médiane/moyenne selon les besoins).
Cela limite le bruit des valeurs extrêmes et met en évidence les régimes dominants plutôt que les valeurs isolées.
Les scénarios sont décrits dans `meteo/correlation_presets.py` et exécutés via un helper générique (`meteo.plots.hexbin`) pour rester réutilisables ailleurs dans le dépôt.
```shell
python "docs/07 - Corrélations multiples/scripts/plot_hexbin_explorations.py"
```
### Température vs humidité — couleur = pluie (max)
### Température, humidité et pluie
Plafond d'humidité quasi systématique sous 510 °C, et même quand la température remonte, la pluie ne survient que dans une bande 816 °C où l'humidité reste >90 %. L'agrégat `max` met en valeur les épisodes pluvieux rares.
Sur ce graphique, laxe horizontal montre la température, laxe vertical lhumidité, et la couleur indique la pluie maximale observée dans chaque case.
On voit un plafond d'humidité quasi systématique sous 510 °C, et même quand la température remonte, la pluie ne survient que dans une bande 816 °C où l'humidité reste >90 %.
L'agrégat `max` met en valeur les épisodes pluvieux rares, qui ressortent comme des taches plus sombres dans ce « nuage » autrement très dense.
![Température vs humidité, couleur pluie](./figures/hexbin_explorations/hexbin_temp_humidity_color_rain.png)
### Température vs élévation solaire — couleur = humidité (médiane)
### Soleil, température et humidité
La courbe en cloche suit le soleil : températures les plus hautes autour de 60° d'élévation, tandis que l'humidité médiane dégringole dès que le soleil est positif puis remonte au crépuscule, ce qui matérialise l'assèchement diurne.
Ici, on place lélévation solaire en abscisse, la température en ordonnée, et on colore chaque hexagone par lhumidité médiane.
La courbe en cloche suit le soleil : températures les plus hautes autour de 60° d'élévation, tandis que l'humidité médiane dégringole dès que le soleil est positif puis remonte au crépuscule.
On visualise ainsi, en un seul coup dœil, lassèchement diurne classique : plus le soleil monte, plus lair se réchauffe et plus lhumidité relative chute.
![Température vs élévation solaire, couleur humidité](./figures/hexbin_explorations/hexbin_sunelev_temp_color_humidity.png)
### Pression vs pluie — couleur = vent (médiane)
### Pression, pluie et vent
La matrice est clairsemée (pluie rare), mais les cases actives se concentrent sous ~1015 hPa avec des médianes de vent plus élevées, signe que les épisodes pluvieux et venteux coïncident surtout avec des pressions modestes.
Dans cette vue, la pression est en abscisse, le taux de pluie en ordonnée, et la couleur représente la vitesse médiane du vent.
La matrice est clairsemée (la pluie est rare), mais les cases actives se concentrent sous ~1015 hPa avec des médianes de vent plus élevées.
On retrouve un schéma familier : les épisodes pluvieux un peu ventés se produisent surtout sous des pressions modestes, tandis que les hautes pressions restent largement calmes et sèches.
![Pression vs pluie, couleur vent](./figures/hexbin_explorations/hexbin_pressure_rain_color_wind.png)
### Humidité vs illuminance — couleur = température (moyenne)
### Lumière, humidité et température
Deux régimes se détachent : nuits très humides et fraîches (illuminance proche de zéro), journées sèches et plus chaudes. La température moyenne colorée rend visibles les transitions plus fraîches sous ciel couvert.
On regarde ici lilluminance (lumière reçue) en abscisse, lhumidité en ordonnée, avec la température moyenne en couleur.
Deux régimes se détachent très nettement : nuits très humides et fraîches (illuminance proche de zéro), journées plus sèches et plus chaudes.
Entre les deux, la carte montre des zones dans lesquelles la température reste modérée malgré un peu de lumière, typiques des journées couvertes où lon ne « ressent » pas vraiment la montée du soleil.
![Humidité vs illuminance, couleur température](./figures/hexbin_explorations/hexbin_lux_humidity_color_temp.png)
### Élévation solaire vs illuminance — couleur = pluie (max)
### Soleil, lumière et pluie
Le nuage suit la diagonale « géométrie du soleil → lumière attendue », avec des cases assombries (illuminance faible malgré un soleil haut) où la pluie maximale ressort : on visualise directement l'impact d'un ciel très chargé sur l'apport lumineux.
Dans cette dernière combinaison, lélévation solaire est en abscisse, lilluminance en ordonnée, et la couleur code la pluie maximale.
Le nuage suit la diagonale « géométrie du soleil → lumière attendue » : plus le soleil monte, plus la case devrait être lumineuse.
Les hexagones assombris (illuminance faible malgré un soleil haut) ressortent immédiatement : ce sont les situations dans lesquelles un ciel très chargé bloque la lumière, et où la pluie maximale apparaît.
Cest une façon très visuelle de voir que « soleil haut » ne signifie pas forcément « journée lumineuse ».
![Élévation solaire vs illuminance, couleur pluie](./figures/hexbin_explorations/hexbin_sunelev_lux_color_rain.png)
## Conclusion
Ces hexbins confirment les mécanismes physiques attendus : le rayonnement solaire chauffe et assèche, lhumidité suit la capacité de lair en vapeur, la pluie survient surtout sous pression plus basse et lumière écrasée par les nuages. Mais notre jeu sarrête en novembre : il manque la saison froide, donc les régimes hivernaux (neige, pluies froides, journées très courtes, plafond dhumidité quasi permanent) restent invisibles. Toute généralisation doit tenir compte de cette lacune saisonnière ; il faudrait compléter la série ou intégrer des données externes (nébulosité, contexte synoptique) pour confirmer ces motifs en hiver.
Ces hexbins confirment les mécanismes physiques attendus : le rayonnement solaire chauffe et assèche, lhumidité suit la capacité de lair en vapeur, la pluie survient surtout sous pression plus basse et lumière écrasée par les nuages.
Mais notre jeu sarrête en novembre : il manque la saison froide, donc les régimes hivernaux (neige, pluies froides, journées très courtes, plafond dhumidité quasi permanent) restent invisibles.
Toute généralisation doit tenir compte de cette lacune saisonnière ; il faudra attendre de pouvoir compléter la série ou intégrer des données externes (nébulosité, contexte synoptique) pour confirmer ces motifs en hiver.

View File

@ -1,40 +1,70 @@
# Cadre prédictif local
Objectif : poser les bases dun modèle sur-mesure qui prédit, au pas local de la station, la température, la pluie et le vent à plusieurs horizons (T+10 min, +60 min, +6 h, +24 h).
On garde une approche expérimentale : on cherche à comprendre ce qui fonctionne ou échoue, non pas à atteindre une performance commerciale.
Il ne s'agit pas ici de venir concurrencer Météo France, mais de jouer avec nos données et avec l'IA.
Dans ce chapitre, on quitte le terrain purement descriptif pour tenter quelque chose de plus ambitieux : faire parler la station météo comme un petit modèle de prévision maison.
Lidée est de construire un cadre simple, mais honnête, qui prédise au pas local la température, la pluie et le vent à plusieurs horizons (T+10 min, +60 min, +6 h, +24 h).
On garde une approche expérimentale : on cherche à comprendre ce qui fonctionne, ce qui casse, et pourquoi, et non pas à rivaliser avec les services de prévision nationaux.
Il sagit avant tout de jouer avec nos données et avec lIA, en gardant les pieds sur terre.
## Cibles et horizons
- Température (continue) ; Vitesse du vent (continue) ; Précipitations binaires (pluie ou neige oui/non). Éventuellement : événements extrêmes (forte chaleur/froid, risque dorage) vus comme des seuils.
- Horizons évalués : T+10, T+60, T+360 (~6 h), T+1440 (~24 h) minutes pour voir quand notre modèle montrera ses faiblesses.
Concrètement, on veut prédire trois types de grandeurs.
Dabord des valeurs continues comme la température et la vitesse du vent, pour lesquelles on attend des erreurs exprimées en °C ou en km/h.
Ensuite une variable binaire « pluie oui/non », qui condense les précipitations en un simple événement : y atil eu pluie (ou neige fondue) sur le pas considéré, oui ou non ?
On pourrait prolonger ce cadre vers des événements extrêmes (forte chaleur, coup de froid, rafales, risque dorage) en posant des seuils, mais on reste ici sur ces cibles de base.
Ces cibles sont évaluées à plusieurs horizons pour voir à partir de quand la prévision décroche : T+10 minutes pour le très court terme, T+60 minutes pour lheure qui vient, T+360 minutes (~6 h) pour la demijournée et T+1440 minutes (~24 h) pour léchéance journalière.
On ne sattend pas à ce que le modèle soit bon partout ; justement, comparer les horizons permettra de voir où il commence à perdre pied.
## Métriques
- Température / vent : _MAE_ (_Mean Absolute Error_) = moyenne des écarts en valeur absolue, facile à lire en °C ou km/h ; _RMSE_ (_Root Mean Squared Error_) pénalise davantage les grosses erreurs pour mieux voir les limites du modèle lorsque des écarts importants apparaissent.
- Précipitations binaires : précision (part des annonces de pluie qui étaient justes), rappel (part des pluies réellement captées), _F1_ (compromis précision/rappel), _Brier score_ (qualité des probabilités, plus il est bas mieux cest) et _calibration_ des probabilités (est-ce quun 30 % de pluie signifie vraiment ~30 % des cas).
- Événements extrêmes : même logique précision/rappel sur dépassement de seuils (chaleur/froid/rafale), avec suivi des fausses alertes pour rester prudent.
Pour juger ces prévisions, on a besoin dune grille de lecture commune.
Sur la température et le vent, on utilise deux erreurs classiques : la _MAE_ (_Mean Absolute Error_), qui est en gros la moyenne des écarts en valeur absolue, et la _RMSE_ (_Root Mean Squared Error_), qui pénalise davantage les grosses erreurs (voir par exemple larticle sur l[erreur quadratique moyenne](https://fr.wikipedia.org/wiki/Erreur_quadratique_moyenne)).
La MAE se lit directement en unités physiques (°C, km/h) et donne une intuition simple : « en moyenne, je me trompe de 0,7 °C ».
La RMSE, elle, sert surtout à mettre en avant les scénarios dans lesquels le modèle déraille.
Pour la pluie binaire, on revient aux métriques de classification : précision (proportion des annonces de pluie qui étaient correctes), rappel (part des pluies réellement captées), [_F1_](https://fr.wikipedia.org/wiki/F-mesure) (compromis entre précision et rappel), et [_Brier score_](https://en.wikipedia.org/wiki/Brier_score) (qualité des probabilités, plus il est bas mieux cest).
On sintéresse aussi à la calibration : lorsquon annonce 30 % de pluie, estce quil pleut effectivement dans ~30 % des cas ?
Un modèle mal calibré « surréagit » ou se montre trop timide, même si son F1 est correct.
Pour des événements extrêmes éventuels (rafale, forte chaleur, etc.), on resterait sur la même logique précision/rappel, mais avec un focus particulier sur les fausses alertes : il vaut mieux ne pas déclencher une alerte dès que le modèle a un doute, surtout si lutilisateur est un simple particulier.
## Limites à garder en tête
- Pas dhiver complet dans le jeu actuel (mars→novembre) : les régimes froids et la neige sont absents.
- Aucune info synoptique (pression régionale, nébulosité, vent en altitude) : le modèle reste “aveugle” au contexte large.
- Pluie rare (~4 % des pas), donc classes déséquilibrées pour la partie pluie/orage.
- Pas brut à 10 minutes : bon pour réactivité courte, mais bruité ; on testera aussi des features lissées.
Avant de lancer des modèles, il faut aussi être clair sur **ce quils ne pourront pas faire**.
Notre jeu de données ne couvre que mars à novembre : toute la saison froide manque, avec la neige, les pluies froides et les régimes hivernaux typiques.
La station ne voit que son environnement immédiat : elle na aucune information sur la situation synoptique (pression régionale, nébulosité large, vent en altitude), ce qui la laisse quasiment aveugle au contexte qui pilote la météo à grande échelle.
La pluie est rare (~4 % des pas de temps), ce qui crée un fort déséquilibre de classes pour la partie pluie/orage.
Enfin, le pas brut de 10 minutes est pratique pour la réactivité, mais assez bruité : il faudra tester des variables un peu lissées pour ne pas nourrir les modèles avec du « bruit blanc ».
Ces contraintes ne rendent pas lexercice inutile ; elles fixent simplement la barre de ce que lon peut raisonnablement espérer.
Tout gain devra se lire à laune de ces limites.
## Données et découpes
- Source principale : `data/weather_minutely.csv` (pas 10 min), enrichissable au fil du temps. On peut réutiliser les CSV dérivés des chapitres précédents (matrices de lags/corrélations du chapitre 5, notamment) pour guider les lags utiles ou vérifier la cohérence.
- Découpe temporelle sans fuite : partie _train_ (début→~70 %), partie validation (~15 % suivant), partie test finale (~15 % le plus récent). Variante : _time-series split_ “en rouleau”, où lon répète ce découpage plusieurs fois ; on appelle _fold_ chaque paire (train, validation) ainsi construite.
- Normalisation/standardisation : on calcule les paramètres (par exemple moyenne et écart-type) uniquement sur la partie _train_, puis on applique ces mêmes paramètres à la validation et au test. Cela évite dintroduire, par mégarde, des informations issues du futur dans les étapes de préparation des données.
Le terrain de jeu reste le même : `data/weather_minutely.csv`, le dataset minuté du chapitre 2, mis à jour au fil du temps.
On peut y rattacher les CSV dérivés des chapitres précédents (matrices de lags et de corrélations décalées du chapitre 5, notamment) pour guider les choix de variables et vérifier que les signaux utilisés restent cohérents.
## Variables dérivées de base (simples et explicables)
Pour évaluer un modèle, on découpe cette série temporelle en trois morceaux successifs : une partie _train_ qui couvre environ les 70 % premiers points, une partie validation (~15 % suivant) qui sert à choisir les hyperparamètres sans toucher au test, et enfin une partie test (~15 % les plus récents) qui joue le rôle de futur inconnu.
Tout est gardé dans lordre chronologique pour éviter dutiliser des informations à rebours.
Une variante plus robuste consiste à utiliser un _time-series split_ « en rouleau » : on répète plusieurs fois ce découpage en faisant glisser la fenêtre dapprentissage/validation dans le temps, chaque couple (_train_, validation) formant alors un _fold_.
La normalisation (ou standardisation) se fait, elle aussi, de façon prudente : on calcule les paramètres (par exemple moyenne et écarttype) uniquement sur la partie _train_, puis on applique ces mêmes paramètres à la validation et au test.
Cela évite de « voir le futur » lors de la préparation des données, ce qui fausserait immédiatement lévaluation.
## Variables dérivées de base
- Temps (_sin_/_cos_) : lheure et le jour de lannée sont périodiques ; représenter lheure avec _sin_/_cos_ évite un faux saut entre 23h et 0h ou entre 31 déc et 1er jan. On encode ainsi heure/minute sur 24 h et jour de lannée sur 365 j.
- Lags courts : valeurs à T-10, -20, -30 min pour chaque variable cible ; deltas (T0 T-10) pour décrire la tendance récente (la “pente”) : estce que la température, le vent ou la pression augmentent ou diminuent, et à quelle vitesse. Les lags analysés au chapitre 5 serviront dinspiration.
- Moyennes glissantes : moyenne ou médiane sur 3060 min pour lisser le bruit ; cumul de pluie sur 3060 min pour connaître létat “humide” récent.
- Composantes vent : (u, v) = (speed _ *sin*(direction), speed _ _cos_(direction)) pour représenter la direction sans discontinuité 0/360°.
- Drapeaux dévénements : pluie*en_cours (rain_rate > 0), vent_fort (seuil), chaleur/froid (seuils). Peuvent servir de \_features* et de cibles dérivées.
- Composantes vent : on projette la vitesse et la direction du vent en deux composantes cartésiennes `(u, v)` avec `u = speed * sin(direction)` et `v = speed * cos(direction)`. Cela permet de représenter la direction sans discontinuité artificielle entre 0° et 360° (un vent de 359° reste ainsi très proche dun vent de 1°).
- Drapeaux dévénements : créer des variables booléennes comme `pluie_en_cours` (quand `rain_rate > 0`), `vent_fort` (audelà dun certain seuil) ou `chaleur`/`froid` (audelà/endeçà de seuils de température). Ces indicateurs peuvent servir de _features_ supplémentaires pour les modèles, mais aussi de cibles dérivées lorsque lon sintéresse à des événements plutôt quà des valeurs exactes.
## Références simples (points de comparaison)
@ -45,8 +75,8 @@ Il ne s'agit pas ici de venir concurrencer Météo France, mais de jouer avec no
## Modèles à introduire dans les chapitres suivants
- Modèles linéaires avec régularisation (_Ridge_/_Lasso_) pour températures/vents : même formule que la régression linéaire classique, mais avec un terme supplémentaire qui limite lampleur des coefficients pour réduire le surapprentissage (_Ridge_ pénalise surtout les coefficients trop grands, _Lasso_ peut en forcer certains à zéro).
- Régression logistique pour la pluie : produit une probabilité de pluie plutôt quun oui/non brut, ce qui permet ensuite de choisir un seuil de décision adapté à lusage (plutôt prudent ou plutôt conservateur).
- Si besoin de courbes plus flexibles : arbres peu profonds, _random forest_ ou _boosting_ légers pour capturer des relations non linéaires sans rendre le modèle complètement opaque.
- Modèles linéaires avec régularisation ([_Ridge_](https://en.wikipedia.org/wiki/Ridge_regression)/[_Lasso_](<https://en.wikipedia.org/wiki/Lasso_(statistics)>)) pour températures/vents : même formule que la régression linéaire classique, mais avec un terme supplémentaire qui limite lampleur des coefficients pour réduire le surapprentissage (_Ridge_ pénalise surtout les coefficients trop grands, _Lasso_ peut en forcer certains à zéro).
- [Régression logistique](https://fr.wikipedia.org/wiki/R%C3%A9gression_logistique) pour la pluie : produit une probabilité de pluie plutôt quun oui/non brut, ce qui permet ensuite de choisir un seuil de décision adapté à lusage (plutôt prudent ou plutôt conservateur).
- Si besoin de courbes plus flexibles : arbres peu profonds, [_random forest_](https://fr.wikipedia.org/wiki/For%C3%AAt_al%C3%A9atoire) ou [_gradient boosting_](https://fr.wikipedia.org/wiki/Gradient_boosting) légers pour capturer des relations non linéaires sans rendre le modèle complètement opaque .
- Évaluation multi-horizons avec _time-series split_, courbes derreur en fonction de lhorizon pour voir à partir de quand le modèle décroche.
- Pipeline dinférence local (_pipeline_ de prédiction) : charger le dernier point, générer les variables dérivées, prédire T+10/+60/+360/+1440, journaliser lerreur au fil du temps pour suivre la qualité du modèle.

View File

@ -1,11 +1,32 @@
# Premiers modèles prédictifs
Objectif : passer de la description à la prédiction sur nos données locales, en restant simple et lisible. On compare quelques approches de base sur les horizons T+10, T+60, T+360 (~6 h) et T+1440 (~24 h) pour température, vent et pluie, sans présupposer que ça va marcher à tous les coups.
Objectif : passer de la description à la prédiction sur nos données locales, en restant simple et lisible.
Lidée est de prendre le cadre posé au chapitre 8 et de le peupler avec des modèles très basiques, pour voir ce quils valent réellement sur les horizons T+10, T+60, T+360 (~6 h) et T+1440 (~24 h) pour la température, le vent et la pluie, sans présupposer que “plus complexe” signifie forcément “meilleur”.
## Point de départ : données, features et métriques
On travaille toujours à partir de `data/weather_minutely.csv` (pas 10 minutes), enrichi des variables dérivées vues au chapitre 8 : temps encodé en sin/cos, lags courts (T10/20/30), deltas, moyennes et cumuls glissants sur 3060 minutes, composantes `(u, v)` du vent, drapeaux dévénements (pluie en cours, vent fort, chaleur/froid).
Cette table est découpée en trois blocs chronologiques : une partie _train_ (~70 % du début de la série) pour lapprentissage, une partie validation (~15 % suivante) pour régler les hyperparamètres, et une partie test (~15 % la plus récente) pour juger les modèles sur un futur qui na pas servi à lentraînement.
On peut, en variante, utiliser un _timeseries split_ “en rouleau” comme décrit au chapitre 8, mais les figures de ce chapitre sappuient sur une découpe simple.
Côté métriques, on reste sur les repères déjà introduits : MAE/RMSE pour température et vent, et pour la pluie binaire, le trio précisionrappel[_F1_](https://fr.wikipedia.org/wiki/F-mesure) complété par le [_Brier score_](https://en.wikipedia.org/wiki/Brier_score) et la calibration des probabilités.
Les modèles ne seront jugés intéressants que sils apportent un gain clair par rapport à des références naïves.
## Étape 1 — Baselines de référence
```shell
python "docs/09 - Premiers modèles prédictifs/scripts/run_baselines.py"
```
Ce premier script ne fait quune chose : mesurer la performance de quelques stratégies “bêtes mais honnêtes” qui serviront ensuite de ligne de base.
Il lit le dataset minuté, applique le découpage temporel, puis entraîne et évalue :
- des baselines de persistance (prédire que la prochaine valeur est identique à la dernière observée) ;
- des climatologies horaires (moyennes/quantiles par heure locale, éventuellement par saison) ;
- des moyennes mobiles à court terme ;
- un classifieur “toujours sec” pour la pluie, qui illustre à quel point la rareté des précipitations peut tromper les métriques.
Le script génère :
- deux CSV de résultats dans `docs/09 - Premiers modèles prédictifs/data/` :
@ -15,11 +36,30 @@ Le script génère :
- `baselines_mae_validation.png` (MAE vs horizon pour température et vent)
- `baselines_rain_validation.png` (F1 et Brier vs horizon pour la pluie)
Ces deux graphiques résument ce que ces baselines savent faire, horizon par horizon.
![](./figures/baselines_mae_validation.png)
![](./figures/baselines_rain_validation.png)
Sur la figure MAE, la persistance reste imbattable jusquà +6 h sur la température, avec une erreur moyenne sous le degré ; à +24 h, elle se dégrade (MAE ≈1,5 °C), mais reste nettement devant la climatologie horaire, qui plafonne autour de 46 °C.
Pour le vent, la moyenne mobile sur 60 minutes devance légèrement la persistance dès +10 min, mais lécart reste modeste et la MAE grimpe doucement avec lhorizon (≈2 km/h à +24 h).
Le message est clair : sur un pas de 10 minutes, les baselines “bricolées” font déjà un travail honorable, et le gain potentiel dun modèle plus riche sera nécessairement limité.
Sur la figure pluie, la persistance profite à plein de la rareté des événements : annoncer “sec” presque tout le temps donne un F1 très élevé aux petits horizons, même si le Brier score se dégrade en séloignant.
La climatologie horaire, sans contexte, ne voit pratiquement jamais la pluie.
Toute tentative de modèle devra donc battre la persistance sur F1/Brier, surtout à +60 et +360 minutes où le score chute déjà : la barre nest pas très haute en absolu, mais elle lest par rapport à la quantité dinformation disponible dans une simple série locale.
Ces baselines fixent donc une référence indispensable : si un modèle plus sophistiqué napporte pas de gain visible par rapport à elles, il ne sert à rien en pratique.
## Étape 2 — Premiers modèles supervisés
```shell
python "docs/09 - Premiers modèles prédictifs/scripts/run_first_models.py"
```
Ce second script :
Une fois les références en place, on passe à des modèles un peu plus élaborés. Ce second script :
- construit les variables dérivées (sin/cos temporels, lags, deltas, moyennes glissantes, vent u/v, drapeaux) à partir du CSV brut ;
- découpe en _train_/_validation_/_test_ (70/15/15 %) ;
@ -28,66 +68,28 @@ Ce second script :
- exporte `models_regression.csv` et `models_rain.csv` dans `docs/09 - Premiers modèles prédictifs/data/` ;
- produit `models_mae_validation.png` (MAE vs horizon pour température et vent) dans `docs/09 - Premiers modèles prédictifs/figures/`.
## Données et préparation
![](./figures/models_mae_validation.png)
- Jeu principal : `data/weather_minutely.csv` (pas 10 min), mis à jour au fil du temps. On peut réutiliser les CSV dérivés (matrices de lags/corrélations du chapitre 5) pour choisir des lags pertinents et vérifier la cohérence.
- Variables dérivées reprises du chapitre 8 : temps en _sin_/_cos_, lags courts (T-10/-20/-30), deltas (variation récente), moyennes/cumul sur 3060 min, composantes (u, v) du vent, drapeaux dévénements (pluie en cours, vent fort, chaleur/froid).
- Normalisation : on calcule moyenne/écart-type sur la partie _train_ uniquement, puis on applique ces paramètres aux parties _validation_ et _test_ pour ne pas utiliser dinformations futures.
Cette figure concentre la lecture sur température et vent, en MAE vs horizon.
## Découpage et validation
Côté température, Ridge/Lasso battent légèrement la persistance sur tous les horizons sauf à +10 min (MAE ≈0,14 à +60 min vs 0,15 pour la persistance ; ≈1,48 à +1440 vs 1,55).
Injecter les variables corrélées (humidité, illuminance, pression…) donne un petit gain par rapport à la version “lags génériques”, mais la marge reste modeste.
- Découpe sans fuite : _train_ (début→~70 %), _validation_ (~15 % suivant), _test_ (~15 % le plus récent), tout en ordre chronologique.
- Variante robuste : _time-series split_ “en rouleau”, où lon répète plusieurs découpes successives ; chaque paire (_train_, _validation_) est un _fold_. Cela aide à voir si un modèle reste stable dans le temps.
Pour le vent, la logique est la même : un léger mieux que la persistance (≈0,87 à +10 min vs 0,99 ; ≈1,67 à +1440 vs 1,74), sans révolution.
Les corrélations étant globalement faibles, lapport des autres variables reste limité sur ce pas de 10 minutes.
## Références de comparaison (_baselines_)
Pour la pluie, la régression logistique ne parvient pas encore à dépasser la persistance : F1 ≈0,91 à +10 min contre 0,94 pour la persistance, puis chute rapide à +60 min et audelà.
La probabilité produite est pourtant correctement calibrée (Brier ≈0,011 à +10 min), mais cela ne suffit pas à compenser lavantage dun modèle qui “reste sec” la plupart du temps.
Il faudra enrichir les variables dentrée ou changer de famille de modèles pour espérer dépasser cette baseline très difficile à battre sur une série aussi déséquilibrée.
- Persistance : prédire que la prochaine valeur est identique à la dernière observée (par horizon).
- Climatologie horaire : moyenne ou quantiles par heure locale (et éventuellement par saison) pour température/vent ; fréquence de pluie par heure pour la pluie.
- Moyenne mobile : prolonger la moyenne des 3060 dernières minutes.
- Pluie rare : classifieur “toujours sec” comme seuil minimal ; si un modèle ne fait pas mieux, il ne sert à rien.
En résumé, ces premiers modèles linéaires apportent un petit gain sur température/vent, mais échouent encore à battre la persistance pour la pluie.
Ils fixent un second niveau de référence : toute complexité supplémentaire devra se justifier par un gain clair, surtout sur les horizons intermédiaires (+60/+360 min) où les baselines commencent à se dégrader.
## Modèles simples à essayer
## Conclusion
- Régressions linéaires avec régularisation (_Ridge_/_Lasso_) pour température et vent : même principe que la régression linéaire, avec un terme qui limite lampleur des coefficients (_Ridge_) ou peut en annuler certains (_Lasso_) pour éviter le sur-apprentissage.
- Régression logistique pour la pluie : fournit une probabilité de pluie plutôt quun oui/non, ce qui permet dajuster le seuil selon lusage (prudence ou non).
- Si besoin de non-linéarités : petits arbres de décision, _random forest_ ou _boosting_ légers pour capturer des relations plus courbes tout en restant interprétables.
Contre lintuition, cest au très court terme que nos modèles simples se heurtent à un mur pour la pluie : la persistance reste devant à +10 min, et lécart se creuse déjà à +60 min.
## Lecture des résultats
Pour la température et le vent, les gains existent mais restent modestes, même à +10 min, alors quon pouvait espérer que ces prévisions soient “faciles”.
Les horizons longs se dégradent comme prévu, mais le vrai défi est donc daméliorer les prédictions proches sans sur-complexifier.
- Température / vent : _MAE_ et _RMSE_ (définis au chapitre 8) pour juger lerreur moyenne et la sensibilité aux grosses erreurs.
- Pluie : précision, rappel, _F1_, _Brier score_ et calibration des probabilités pour voir si les annonces de pluie sont réalistes et bien calibrées.
- Multi-horizons : tracer lerreur en fonction de lhorizon pour identifier à partir de quand la prévision décroche. On sattend à ce que +24 h soit difficile sans contexte synoptique, et on documentera ces limites.
## Déroulé proposé
1. Construire les variables dérivées et sauvegarder un jeu prêt pour lapprentissage (en suivant le découpage temporel).
2. Évaluer les références (_baselines_) sur chaque horizon.
3. Entraîner les modèles simples (linéaires régularisés, logistique, éventuellement arbres légers) et comparer aux références.
4. Consolider lévaluation multi-horizons (_time-series split_), conserver les résultats pour les chapitres suivants (affinements et pipeline dinférence local).
## Synthèse visuelle des baselines (validation)
![MAE des baselines par horizon](./figures/baselines_mae_validation.png)
![F1 et Brier des baselines pluie](./figures/baselines_rain_validation.png)
### Ce que montrent ces baselines
- Température : la persistance reste imbattable jusquà +6 h avec une MAE < 1 °C ; au-delà (+24 h), lerreur grimpe (1,5 °C) mais reste meilleure que la climatologie horaire qui plafonne autour de 46 °C. On part donc avec un avantage net sur le très court terme, mais lhorizon journalier sera plus difficile.
- Vent : la moyenne mobile 60 min devance légèrement la persistance dès +10 min, mais lécart reste faible et lerreur croît avec lhorizon (MAE ≈2 km/h à +24 h). Le gain potentiel dun modèle plus riche sera modeste si lon reste sur ce pas de 10 min.
- Pluie (binaire) : la persistance affiche des F1 élevés aux petits horizons parce que la pluie est rare et que “rester sec” gagne souvent ; le Brier augmente avec lhorizon, signe que la confiance se dégrade. La climatologie horaire est nulle : sans contexte, elle ne voit pas la pluie. Toute tentative de modèle devra donc battre la persistance sur F1/Brier, surtout à +60/+360 min où le score chute déjà.
- Conclusion provisoire : les baselines définissent une barre à franchir — forte sur le très court terme (température/vent), beaucoup plus basse pour la pluie (où la rareté favorise la persistance). Les modèles devront prouver un gain net sur ces repères, en particulier sur les horizons intermédiaires (+60/+360 min) où la prévisibilité commence à décrocher.
## Premiers modèles (Ridge/Lasso/logistique)
![MAE des modèles (validation)](./figures/models_mae_validation.png)
- Température : Ridge/Lasso battent légèrement la persistance sur tous les horizons sauf à +10 min (MAE ≈0,14 à +60 min vs 0,15 pour la persistance ; ≈1,48 à +1440 vs 1,55). Injecter les variables corrélées (humiditié, illumination, pression…) donne un petit gain par rapport à la version “lags génériques”, mais la marge reste modeste.
- Vent : même logique, un léger mieux que la persistance (≈0,87 à +10 min vs 0,99 ; ≈1,67 à +1440 vs 1,74). Les corrélations étant faibles, lapport des autres variables reste limité.
- Pluie : la régression logistique reste derrière la persistance (F1 ≈0,91 à +10 min contre 0,94 pour la persistance ; chute rapide à +60 et au-delà). La probabilité est calibrée (Brier ≈0,011 à +10 min), mais ne compense pas lavantage de “rester sec”. Il faudra enrichir les features ou changer de modèle pour espérer dépasser la baseline.
En résumé, les modèles linéaires apportent un petit gain sur température/vent et échouent encore à battre la persistance pour la pluie. Cest une base de référence ; les prochains essais devront justifier leur complexité par un gain clair, surtout sur les horizons où les baselines se dégradent (+60/+360 min).
## Conclusion provisoire du chapitre
Contre lintuition, cest au très court terme que nos modèles simples se heurtent à un mur pour la pluie : la persistance reste devant à +10 min, et lécart se creuse déjà à +60 min. Pour la température et le vent, les gains existent mais restent modestes, même à +10 min, alors quon pouvait espérer les “faciles”. Les horizons longs se dégradent comme prévu, mais le vrai défi est donc daméliorer les prédictions proches sans sur-complexifier. Prochaine étape : tester des modèles plus flexibles (arbres/boosting) et enrichir les features, tout en vérifiant que le gain sur les petits horizons justifie leffort.
Prochaine étape : tester des modèles plus flexibles (arbres/boosting) et enrichir les features, tout en vérifiant que le gain sur les petits horizons justifie leffort.

View File

@ -1,12 +1,12 @@
# Modèles non linéaires (arbres, forêts, gradient boosting)
Objectif : tester des modèles plus flexibles que les régressions linéaires/logistiques, en restant raisonnables côté ressources. On utilise des forêts aléatoires (_random forest_) et du _gradient boosting_ sur les mêmes horizons (T+10, T+60, T+360, T+1440) pour température, vent et pluie.
Objectif : tester des modèles plus flexibles que les régressions linéaires/logistiques, en restant raisonnables côté ressources. On utilise des forêts aléatoires (_random forest_, voir [forêt aléatoire](https://fr.wikipedia.org/wiki/For%C3%AAt_al%C3%A9atoire)) et du _gradient boosting_ (voir [gradient boosting](https://fr.wikipedia.org/wiki/Gradient_boosting)) sur les mêmes horizons (T+10, T+60, T+360, T+1440) pour température, vent et pluie, afin de voir si cette complexité supplémentaire paie réellement sur nos données locales.
```shell
python "docs/10 - Modèles non linéaires/scripts/run_tree_models.py"
```
Le script :
Ce script réutilise le même jeu de données minutées que les chapitres précédents et applique un pipeline très proche, en changeant simplement la famille de modèles. Le déroulé est le suivant :
- lit `data/weather_minutely.csv` et construit les variables dérivées (sin/cos, lags/deltas/moyennes, vent u/v, drapeaux) ;
- sappuie sur la matrice de corrélation décalée (chapitre 5) pour prioriser les variables/lags avec |r| ≥ 0,2, tout en conservant les cibles ;
@ -28,9 +28,10 @@ Le script :
- Vent : gains modestes, MAE ~0,94 à +10 min (GB) et ~1,19 à +60 min, sans dépassement clair des modèles linéaires précédents.
- Pluie : F1 ≈0,85 (forêt) et 0,67 (GB) à +10 min, mais toujours en dessous de la persistance (~0,94) ; le Brier reste modéré (~0,020,03). Aux horizons +60/+360/+1440, les scores retombent rapidement.
## Conclusion provisoire
## Conclusion
Ces modèles non linéaires apportent de la flexibilité mais, avec un apprentissage allégé pour tenir le temps de calcul, ils ne battent pas les baselines ni les modèles linéaires sur les horizons courts. Pour progresser, il faudra soit élargir léchantillon dapprentissage (temps de calcul plus long), soit régler finement les hyperparamètres, soit enrichir les features (ou combiner les deux).
Ces modèles non linéaires apportent de la flexibilité mais, avec un apprentissage allégé pour tenir le temps de calcul, ils ne battent pas les baselines ni les modèles linéaires sur les horizons courts.
Pour progresser, il faudra soit élargir léchantillon dapprentissage (temps de calcul plus long), soit régler finement les hyperparamètres, soit enrichir les features (ou combiner les deux).
À ce stade, les modèles non-linéaires "naïfs" que l'on a implémenté ici travaillent pendant plusieurs minutes et ne sont pas capables de faire mieux que les modèles vus précédemment.
Je doute donc qu'il soit pertinent de creuser le sujet, mais cela a aiguisé ma curiosité pour des modèles existants, pré-entraînés, tels que Chronos, d'Amazon.
Je doute donc qu'il soit pertinent de creuser bien davantage cette piste dans le cadre local actuel, mais cela a aiguisé ma curiosité pour des modèles existants, pré-entraînés, tels que Chronos d'Amazon, que lon testera au chapitre suivant.

View File

@ -1,23 +1,43 @@
# Modèle Chronos-2 (foundation model HF)
Objectif : tester un modèle de prévision généraliste récent (**Chronos-2**, Amazon) en zéro-shot sur notre station. On resample la température à lheure, on coupe les dernières 96 h pour évaluer la prévision, et on compare le forecast à lobservé.
Dans les chapitres précédents, on a testé des modèles “fabriqués maison” (linéaires, arbres, forêts, boosting) sur nos données locales.
Ici, on bascule dans un autre monde : celui des modèles de prévision préentraînés sur de vastes jeux de séries temporelles.
**Chronos2**, développé par Amazon et distribué notamment via [Hugging Face](https://huggingface.co/amazon/chronos-t5-small) et le dépôt [chronosforecasting](https://github.com/amazon-science/chronos-forecasting), est un modèle de type “foundation” capable de produire des prévisions probabilistes sur des séries très variées.
Lobjectif est de le tester en zéroshot sur notre station : lui fournir uniquement lhistorique local (sans réentraînement), lui demander une prévision et voir comment il se comporte face aux observations et aux modèles précédents.
## Première passe : choix de la taille du modèle (horizon 96 h)
On commence par un exercice simple : prédire les 96 dernières heures de température à partir des 336 heures précédentes, en faisant en resampling de la série à lheure.
On coupe donc les 96 h finales, on fournit à Chronos le contexte horaire, et on compare sa prévision aux observations.
```shell
python "docs/11 - Modèle Chronos/scripts/run_chronos.py"
```
### Résultats comparés
Ce script sappuie sur quelques variables denvironnement pour contrôler le comportement du modèle :
Nous avons utilisé les variables denvironnement suivantes : `CHRONOS_CONTEXT` (336 h), `CHRONOS_HORIZON` (96 h), `CHRONOS_RESAMPLE` (`1h`), `CHRONOS_SAMPLES` (20).
- `CHRONOS_CONTEXT` : longueur du contexte temporel fourni au modèle (ici 336 h, soit 14 jours) ;
- `CHRONOS_HORIZON` : horizon de prévision demandé (96 h) ;
- `CHRONOS_RESAMPLE` : pas temporel utilisé (`1h`) ;
- `CHRONOS_SAMPLES` : nombre déchantillons générés pour lisser la prévision (20).
![Comparaison des tailles Chronos](./figures/chronos_models_comparison.png)
Sur la même fenêtre de validation locale, nous avons testé trois tailles (`CHRONOS_MODEL`) : `chronos-t5-mini`, `chronos-t5-small` et `chronos-t5-base`. Le modèle **small** semble le plus précis (MAE ≈ 3,68 °C, RMSE ≈ 4,53 °C), les versions mini et base étant derrière (MAE ≈ 4,184,24 °C, RMSE ≈ 5,35,6 °C). Autrement dit, monter en taille na pas amélioré la prévision à 96 h sur ces données locales ; la version `small` offre le meilleur compromis précision/poids.
Sur cette même fenêtre de validation locale, on teste trois tailles de modèle (`CHRONOS_MODEL`) : `chronos-t5-mini`, `chronos-t5-small` et `chronos-t5-base`. Le script de comparaison :
```shell
python "docs/11 - Modèle Chronos/scripts/compare_chronos.py"
```
produit la figure suivante, qui résume la performance par taille.
![Comparaison des tailles Chronos](./figures/chronos_models_comparison.png)
Le modèle **small** ressort comme le meilleur compromis : MAE ≈ 3,68 °C, RMSE ≈ 4,53 °C, alors que les versions mini et base sont derrière (MAE ≈ 4,184,24 °C, RMSE ≈ 5,35,6 °C) sur cette fenêtre.
Autrement dit, “monter en taille” naméliore pas la prévision à 96 h sur nos données ; Chronossmall fait ici légèrement mieux que ses voisins, pour un coût de calcul encore raisonnable.
On peut ensuite inspecter, pour chaque taille, la trajectoire temporelle prédit vs observé :
#### chronos-t5-mini
![Trajectoire prédit vs observé mini](./figures/chronos_forecast_amazon__chronos-t5-mini.png)
@ -26,26 +46,40 @@ python "docs/11 - Modèle Chronos/scripts/compare_chronos.py"
![Trajectoire prédit vs observé small](./figures/chronos_forecast_amazon__chronos-t5-small.png)
Le modèle suit un peu mieux la courbe de référence que les autres, mais une forte amplitude existe.
Le modèle small suit un peu mieux la courbe de référence que les autres, même si lamplitude reste importante à 96 h.
#### chronos-t5-base
![Trajectoire prédit vs observé base](./figures/chronos_forecast_amazon__chronos-t5-base.png)
### Lecture et portée
Au terme de cette première passe, on retient donc `chronos-t5-small` comme taille “par défaut” pour la suite des expériences.
Pour coller à nos horizons cibles, `run_chronos_multi.py` évalue Chronos-small sur 1 h, 6 h et 24 h pour la température, le vent et la pluie (horaire uniquement ; lhorizon 10 minutes nest pas couvert par Chronos qui est pré-entraîné en pas horaire). Les figures `chronos_multi_temperature.png`, `chronos_multi_wind_speed.png` et `chronos_multi_rain_rate.png` illustrent où le modèle est le plus fiable : à 1 h, la température reste sous ~1,3 °C de MAE et le vent sous ~0,6 km/h ; à 6 h, lerreur grimpe modérément (≈2 °C temp., ≈3 km/h vent) ; à 24 h, elle dépasse 46 (°C/ km/h). Sur la pluie, le F1 reste nul à 1 h/6 h et ne monte quà ~0,15 à 24 h, signe que le modèle “foundation” horaire ne capture pas bien les occurrences locales rares.
## Évaluation multi-horizons locale (1 h, 6 h, 24 h)
Pour comparer plus finement à notre cadre prédictif local, on évalue ensuite Chronossmall sur des horizons plus courts, en restant au pas horaire (Chronos nest pas préentraîné au pas 10 minutes).
Le script suivant demande au modèle de prévoir la température, le vent et la pluie à 1 h, 6 h et 24 h :
```shell
python "docs/11 - Modèle Chronos/scripts/run_chronos_multi.py"
```
Les trois figures suivantes montrent la performance par horizon :
![Chronos small température (1/6/24 h)](./figures/chronos_multi_temperature.png)
![Chronos small vent (1/6/24 h)](./figures/chronos_multi_wind_speed.png)
![Chronos small pluie (1/6/24 h)](./figures/chronos_multi_rain_rate.png)
On y voit que :
- à 1 h, la température reste sous ~1,3 °C de MAE et le vent sous ~0,6 km/h ;
- à 6 h, lerreur grimpe modérément (≈2 °C pour la température, ≈3 km/h pour le vent) ;
- à 24 h, elle dépasse 46 unités (°C / km/h) ;
- sur la pluie, le F1 reste nul à 1 h/6 h et ne monte quà ~0,15 à 24 h, signe que ce modèle “foundation” horaire ne capture pas bien les occurrences locales rares.
Pour avoir une vue plus synthétique, on combine les erreurs sur plusieurs figures :
```shell
python "docs/11 - Modèle Chronos/scripts/plot_chronos_errors_combined.py"
```
@ -54,7 +88,7 @@ python "docs/11 - Modèle Chronos/scripts/plot_chronos_errors_combined.py"
![Chronos small pluie (F1/Brier)](./figures/chronos_multi_errors_rain.png)
Les figures individuelles permettent de voir la trajectoire prédite vs observée (dans la section précédente) et lerreur par horizon (ci-dessous).
Enfin, un script dédié permet de comparer directement, pour plusieurs tailles de Chronos, lerreur absolue en fonction de lhorizon :
```shell
python "docs/11 - Modèle Chronos/scripts/run_chronos_multi_errors.py"
@ -62,9 +96,17 @@ python "docs/11 - Modèle Chronos/scripts/run_chronos_multi_errors.py"
![Erreur absolue vs horizon mini/small/base](./figures/chronos_errors_combined.png)
Au final, Chronos-small fournit un signal exploitable sur la température et un peu sur le vent pour des horizons courts à intermédiaires, mais reste faible sur la pluie et se dégrade nettement au-delà de 24 h. Une calibration locale, davantage de contexte ou une cible adaptée (pluie binaire calibrée) seraient nécessaires pour en faire un outil de prévision robuste sur toutes les variables.
Au final, dans cette configuration zéroshot, Chronossmall fournit un signal exploitable sur la température (surtout à 16 h) et un peu sur le vent, mais reste faible sur la pluie et se dégrade nettement audelà de 24 h.
Pour en faire un outil de prévision robuste sur toutes les variables, il faudrait envisager une calibration locale, davantage de contexte ou des cibles adaptées (par exemple une pluie binaire mieux calibrée).
### Réglages prudents (contexte 288 h, horizon limité à 64 h, 100 échantillons)
## Réglages plus prudents (contexte 288 h, horizon limité à 64 h, 100 échantillons)
Une troisième série dessais cherche à voir si un Chronossmall “raisonnablement bridé” se comporte mieux quune version trop ambitieuse.
Lidée : moins de contexte, un horizon maximum plus court, mais davantage déchantillons pour lisser la prévision :
```shell
python "docs/11 - Modèle Chronos/scripts/run_chronos_tuned.py"
```
![Chronos small réglé température](./figures/chronos_tuned_temperature.png)
@ -72,9 +114,18 @@ Au final, Chronos-small fournit un signal exploitable sur la température et un
![Chronos small réglé pluie](./figures/chronos_tuned_rain_rate.png)
Avec `run_chronos_tuned.py`, on réduit le contexte (288 h) et lhorizon maximum (64 h) tout en augmentant les échantillons (100). Sur la même fenêtre locale, la température saméliore nettement : MAE ~0,75 °C à 1 h, ~1,27 °C à 6 h, ~3,40 °C à 24 h (vs 1,33/2,02/4,84 auparavant). Le vent progresse surtout à 24 h (≈2,39 contre ~6,38 auparavant), même si le 1 h est moins bon que la première passe. La pluie reste instable : le F1 peut atteindre 0,220,28 à 2448 h mais les scores courts sont peu fiables. Limiter lhorizon à 64 h, raccourcir le contexte et lisser par davantage déchantillons améliorent donc la température et le vent, mais ne suffisent pas à rendre la pluie prédictible.
Avec ces réglages (contexte 288 h, horizon limité à 64 h, 100 échantillons), la température saméliore nettement sur la même fenêtre locale : MAE ~0,75 °C à 1 h, ~1,27 °C à 6 h, ~3,40 °C à 24 h (contre ~1,33/2,02/4,84 auparavant).
### Dernier essai en conditions réelles : prévision sur les 6 dernières heures
Le vent progresse surtout à 24 h (≈2,39 km/h contre ~6,38 km/h initialement), même si lhorizon 1 h est un peu moins bon.
La pluie reste instable : le F1 peut atteindre 0,220,28 à 2448 h, mais les scores à court terme demeurent peu fiables.
En pratique, raccourcir le contexte, limiter lhorizon et augmenter le nombre déchantillons améliore donc nettement la température et le vent, mais ne suffit pas à rendre la pluie réellement prédictible à partir de cette seule station.
## Dernier essai quasi “en conditions réelles” : prévision sur les 6 dernières heures
Pour terminer, on simule une situation dusage : prévoir les 6 prochaines heures à partir de lhistorique immédiatement disponible, sans les connaître à lavance.
On masque donc les 6 dernières heures de la série, on fournit à Chronossmall 288 h de contexte, et on lui demande la prévision correspondante :
```shell
python "docs/11 - Modèle Chronos/scripts/run_chronos_holdout6.py"
@ -82,4 +133,8 @@ python "docs/11 - Modèle Chronos/scripts/run_chronos_holdout6.py"
![Chronos small erreurs 6 h holdout](./figures/chronos_holdout6_errors.png)
On a masqué les 6 dernières heures de la série et demandé à Chronos-small (contexte 288 h, 50 échantillons) de prévoir température, vent (vitesse + direction), humidité et pression, puis comparé aux valeurs réelles. Moyennes des erreurs absolues sur ces 6 pas : température ~0,84 °C, vent ~1,2 km/h, direction ~3,9°, pression ~5,3 hPa, humidité ~24 %. Les erreurs restent très contenues sur température/vent et la direction est raisonnable (quelques degrés) ; la pression dérive (5 hPa) et lhumidité est clairement décalée. Ce test confirme que Chronos peut fournir des tendances crédibles à très court terme (<6 h) sur température et vent, mais pas sur lhumidité (et on a vu plus haut que la pluie reste peu fiable).
Sur ces 6 pas, les moyennes des erreurs absolues sont les suivantes : température ~0,84 °C, vent ~1,2 km/h, direction ~3,9°, pression ~5,3 hPa, humidité ~24 %.
Les erreurs restent très contenues sur température et vent, la direction est raisonnable (quelques degrés de décalage) ; la pression dérive (≈5 hPa) et lhumidité est clairement décalée.
Ce test, modeste, mais concret, confirme que Chronos peut fournir des tendances crédibles à très court terme (<6 h) sur la température et le vent dans ce cadre zéroshot, mais quil reste peu fiable sur lhumidité et la pluie, lon a vu plus haut que les occurrences locales rares sont difficiles à capter pour un modèle généraliste.

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@ -48,3 +48,14 @@ Une simple série locale, aussi bien nettoyée soitelle, ne suffit pas à dé
Ce projet naboutit pas à un modèle de prévision local prêt à lemploi, mais il remplit une autre fonction : celle dun carnet dexploration.
On y voit comment des données réelles, avec leurs trous et leurs biais, peuvent être préparées, visualisées, confrontées à la physique, puis poussées jusquà leurs limites prédictives.
Si un lecteur a envie de reprendre ce travail, dajouter des sources externes, de tester dautres modèles ou simplement de tracer de nouveaux graphiques, tant mieux : la station continue denregistrer, et lhistoire ne sarrête pas ici.
Pour finir sur une note plus légère, un petit script de curiosité :
```shell
python "docs/12 - Conclusion/scripts/plot_weekday_profiles.py"
```
calcule les moyennes de température, dhumidité et de luminance pour chaque jour de la semaine et produit le graphique cidessous.
Dans mon jeu de données, cest le samedi qui ressort comme le jour « le plus agréable » : en moyenne un peu plus chaud, un peu plus lumineux, et moins humide que ses voisins… ce qui tombe plutôt bien pour organiser les activités du weekend.
![Profils moyens par jour de semaine](./figures/weekday_profiles.png)

View File

@ -0,0 +1,79 @@
from __future__ import annotations
from pathlib import Path
import sys
import pandas as pd
PROJECT_ROOT = Path(__file__).resolve().parents[3]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from meteo.dataset import load_raw_csv
from meteo.variables import VARIABLES_BY_KEY, Variable
from meteo.plots import plot_weekday_profiles
CSV_PATH = PROJECT_ROOT / "data" / "weather_minutely.csv"
DOC_DIR = Path(__file__).resolve().parent.parent
OUTPUT_PATH = DOC_DIR / "figures" / "weekday_profiles.png"
# On se concentre sur le ressenti "agréable" : température, humidité, lumière.
VARIABLE_KEYS = ["temperature", "humidity", "illuminance"]
def compute_weekday_means(df: pd.DataFrame, variables: list[Variable]) -> pd.DataFrame:
"""
Calcule, pour chaque jour de semaine (0=lundi,,6=dimanche),
la moyenne des variables fournies.
"""
if df.empty:
return pd.DataFrame(index=range(7))
weekday_index = df.index.dayofweek
columns = [var.column for var in variables]
weekday_means = df.groupby(weekday_index)[columns].mean()
# S'assure que toutes les valeurs 06 sont présentes, même si certaines manquent.
weekday_means = weekday_means.reindex(range(7))
weekday_means.index.name = "weekday"
return weekday_means
def main() -> None:
if not CSV_PATH.exists():
print(f"⚠ Fichier introuvable : {CSV_PATH}")
return
df = load_raw_csv(CSV_PATH)
print(f"Dataset minuté chargé : {CSV_PATH}")
print(f" Lignes : {len(df)}")
print(f" Colonnes : {list(df.columns)}")
print()
variables = [VARIABLES_BY_KEY[key] for key in VARIABLE_KEYS]
weekday_means = compute_weekday_means(df, variables)
output_path = plot_weekday_profiles(
weekday_df=weekday_means,
variables=variables,
output_path=OUTPUT_PATH,
title="Moyennes par jour de semaine",
)
print(f"✔ Profils hebdomadaires exportés : {output_path}")
weekday_labels_long = ["lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"]
for var in variables:
series = weekday_means[var.column]
if series.isna().all():
continue
best_idx = int(series.idxmax())
best_label = weekday_labels_long[best_idx]
best_value = series.max()
unit = f" {var.unit}" if var.unit else ""
print(f"{var.label} maximale en moyenne le {best_label} (≈{best_value:.2f}{unit})")
if __name__ == "__main__":
main()