# 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 : 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.