1

11 KiB

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

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 :

✔ 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 :

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 :

head data/weather_raw_full.csv
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 :

python "docs/02 - Préparation des données/scripts/format_raw_csv.py"
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éé.

head data/weather_formatted_1s.csv
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 :

python "docs/02 - Préparation des données/scripts/fill_formatted_1s.py"
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 :

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.

Pipeline simplifié

Un script tout simple permet de faire automatiquement tout ce qu'on vient de voir. 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.

python -m scripts.refresh_data_pipeline

Vérification des données

On peut s'assurer que plus aucune information n'est manquante :

python "docs/02 - Préparation des données/scripts/check_missing_values.py"
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 :

python "docs/02 - Préparation des données/scripts/describe_minutely_dataset.py"
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 :

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.