1
2025-11-19 17:01:45 +01:00

244 lines
11 KiB
Markdown

# 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.
## Export des données
```shell
python "docs/02 - Préparation des données/scripts/export_station_data.py"
```
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
✔ Export terminé : /Users/richard/Documents/donnees_meteo/data/weather_raw_7d.csv
```
(Le chemin changera sur votre propre machine)
Vérifiez que le fichier est bien créé et qu'il contient des données.
À la place de `export_station_data.py`, on peut aussi lancer :
```shell
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.
Mon fichier complet contient plus d'un million d'enregistrements et pèse 70Mo.
## Ajustements
Le fichier peut être rapidement inspecté avec la commande `head` :
```shell
head data/weather_raw_full.csv
```
```output
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,,,,,,
2025-03-10 09:35:23.162398+00:00,,83.0,,,,,
2025-03-10 09:35:23.164634+00:00,,,,,7.4,,
2025-03-10 09:35:23.170122+00:00,,,,,,256.0,
2025-03-10 09:35:23.183555+00:00,,,,,,,0.0
2025-03-10 09:35:41.211148+00:00,,,,20551.2,,,
2025-03-10 09:36:22.638255+00:00,,,,,12.2,,
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.
Le script suivant s'occupe de regrouper les données de capteurs dont l'enregistrement est proche :
```shell
python "docs/02 - Préparation des données/scripts/format_raw_csv.py"
```
```output
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'>
Après combinaison (1s) : 630171 lignes
✔ Fichier formaté écrit dans : /Users/richard/Documents/donnees_meteo/data/weather_formatted_1s.csv
```
Un nouveau document CSV intermédiaire est donc créé.
```shell
head data/weather_formatted_1s.csv
```
```output
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,,,
2025-03-10 09:36:22+00:00,,,,20247.6,12.2,306.0,
2025-03-10 09:36:52+00:00,,,,20199.6,9.3,246.0,
2025-03-10 09:37:22+00:00,,,,20034.0,7.9,,
2025-03-10 09:37:52+00:00,,,,20124.0,7.4,284.0,
2025-03-10 09:38:22+00:00,,,,19860.0,9.7,215.0,
2025-03-10 09:39:22+00:00,,,,19722.0,11.4,203.0,
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.
On fait donc :
```shell
python "docs/02 - Préparation des données/scripts/fill_formatted_1s.py"
```
```output
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
```
## 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 :
- 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.
Ces opérations sont réalisées par :
```shell
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.
On peut s'assurer que plus aucune information n'est manquante :
```shell
python "docs/02 - Préparation des données/scripts/check_missing_values.py"
```
```output
Dataset chargé : data/weather_minutely.csv
Lignes : 321881
Colonnes : ['temperature', 'humidity', 'pressure', 'illuminance', 'wind_speed', 'wind_direction', 'rain_rate']
=== Synthèse des valeurs manquantes ===
Total de cellules : 2253167
Cellules manquantes : 0
Fraction manquante : 0.000000
Lignes complètes : 321881
Lignes avec des trous : 0
Fraction lignes complètes : 1.000000
Valeurs manquantes par colonne :
- temperature : 0
- humidity : 0
- pressure : 0
- illuminance : 0
- wind_speed : 0
- wind_direction : 0
- rain_rate : 0
✔ Aucune valeur manquante dans le dataset minuté.
```
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
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
=== describe() ===
temperature humidity pressure ... wind_speed wind_direction rain_rate
count 321881.000000 321881.000000 321881.000000 ... 321881.000000 321881.000000 321881.000000
mean 15.004488 74.131993 1010.683189 ... 2.877190 181.977411 0.108216
std 6.349077 18.885843 8.210283 ... 3.151080 88.089334 0.820691
min -2.200000 20.000000 976.973123 ... 0.000000 0.000000 0.000000
25% 10.277778 59.000000 1005.420000 ... 0.000000 96.000000 0.000000
50% 14.600000 77.666667 1011.514287 ... 2.333549 210.000000 0.000000
75% 19.000000 91.000000 1015.900000 ... 4.650000 247.666196 0.000000
max 34.888889 99.000000 1033.187174 ... 26.554176 360.000000 42.672000
[8 rows x 7 columns]
=== Min / max avec dates ===
- temperature:
min = -2.2 à 2025-03-17 05:16:00+00:00
max = 34.8888888888889 à 2025-07-02 15:59:00+00:00
- humidity:
min = 20.0 à 2025-04-30 15:22:00+00:00
max = 99.0 à 2025-03-11 06:29:00+00:00
- pressure:
min = 976.973122738378 à 2025-10-23 05:06:00+00:00
max = 1033.18717416804 à 2025-10-10 17:12:00+00:00
- illuminance:
min = 0.0 à 2025-03-10 17:44:00+00:00
max = 133520.394 à 2025-07-29 11:48:00+00:00
- wind_speed:
min = 0.0 à 2025-03-10 14:31:00+00:00
max = 26.554176 à 2025-06-26 00:10:00+00:00
- wind_direction:
min = 0.0 à 2025-03-12 04:57:00+00:00
max = 360.0 à 2025-03-12 07:33:00+00:00
- rain_rate:
min = 0.0 à 2025-03-10 09:35:00+00:00
max = 42.672 à 2025-06-15 03:10:00+00:00
=== Vérification de la continuité temporelle ===
Différences d'intervalle (top 5):
time
0 days 00:01:00 304291
0 days 00:02:00 9426
0 days 00:03:00 3562
0 days 00:04:00 1740
0 days 00:05:00 1142
Name: count, dtype: int64
Nombre d'intervalles ≠ 60s : 17589
```
Ces écarts peuvent être identifiés avec le script suivant :
```shell
python "docs/02 - Préparation des données/scripts/list_time_gaps.py"
```
```
Dataset minuté chargé : data/weather_minutely.csv
Lignes : 321881
=== Gaps temporels détectés ===
Nombre de gaps : 17589
Total minutes manquantes (théoriques) : 40466
Top 10 des gaps les plus longs :
- De 2025-06-21 19:09:00+00:00 à 2025-06-21 20:10:00+00:00 (durée: 0 days 01:01:00, manquants: 60, de 2025-06-21 19:10:00+00:00 à 2025-06-21 20:09:00+00:00)
- De 2025-08-10 22:17:00+00:00 à 2025-08-10 23:15:00+00:00 (durée: 0 days 00:58:00, manquants: 57, de 2025-08-10 22:18:00+00:00 à 2025-08-10 23:14:00+00:00)
- De 2025-09-24 20:34:00+00:00 à 2025-09-24 21:32:00+00:00 (durée: 0 days 00:58:00, manquants: 57, de 2025-09-24 20:35:00+00:00 à 2025-09-24 21:31:00+00:00)
- De 2025-06-21 10:58:00+00:00 à 2025-06-21 11:55:00+00:00 (durée: 0 days 00:57:00, manquants: 56, de 2025-06-21 10:59:00+00:00 à 2025-06-21 11:54:00+00:00)
- De 2025-07-10 07:17:00+00:00 à 2025-07-10 08:14:00+00:00 (durée: 0 days 00:57:00, manquants: 56, de 2025-07-10 07:18:00+00:00 à 2025-07-10 08:13:00+00:00)
- De 2025-07-24 03:52:00+00:00 à 2025-07-24 04:46:00+00:00 (durée: 0 days 00:54:00, manquants: 53, de 2025-07-24 03:53:00+00:00 à 2025-07-24 04:45:00+00:00)
- De 2025-10-28 08:31:00+00:00 à 2025-10-28 09:23:00+00:00 (durée: 0 days 00:52:00, manquants: 51, de 2025-10-28 08:32:00+00:00 à 2025-10-28 09:22:00+00:00)
- De 2025-03-16 15:31:00+00:00 à 2025-03-16 16:20:00+00:00 (durée: 0 days 00:49:00, manquants: 48, de 2025-03-16 15:32:00+00:00 à 2025-03-16 16:19:00+00:00)
- De 2025-06-21 12:22:00+00:00 à 2025-06-21 13:08:00+00:00 (durée: 0 days 00:46:00, manquants: 45, de 2025-06-21 12:23:00+00:00 à 2025-06-21 13:07:00+00:00)
- 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.
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.
Il faut être capable de les identifier, et il faut les prendre en compte dans tout calcul ultérieur.
Une fois que tout est passé en revue, on passe d'un jeu contenant plus d'un million d'enregistrements à un jeu n'en contenant plus que 300 000.