From 5b5c97532d6fb55165b5e1dbfcd0d40c731e5041 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Tue, 22 Sep 2026 14:10:35 +0200 Subject: [PATCH] fix(ml): type is_working_hours pour que LightGBM accepte une lecture sans valeur `reading.is_working_hours` est nullable et fait partie des features. Une seule lecture a NULL dans la fenetre suffisait a rendre la colonne `object` au retour de `pd.read_sql`, et LightGBM refuse alors de construire son Dataset : "pandas dtypes must be int, float or bool". La panne n'arrivait qu'au scoring, sur la vraie base, jamais en test. `_typer` coerce donc aussi cette colonne, comme les colonnes mesurees : NaN vaut valeur manquante, que LightGBM gere nativement. Effet de bord voulu, les deux chargeurs rendent enfin le meme dtype : `load_from_csv` faisait un `astype(bool)` qui ecrasait silencieusement une valeur absente en `False`. --- ml/enervision_ml/data.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ml/enervision_ml/data.py b/ml/enervision_ml/data.py index f8e7bf9..51ae047 100644 --- a/ml/enervision_ml/data.py +++ b/ml/enervision_ml/data.py @@ -43,6 +43,10 @@ NUMERIC_COLUMNS = [ "capacity_kw", ] +# Piege : `reading.is_working_hours` est nullable et entre dans les features. Une seule lecture a +# NULL rend la colonne `object`, que LightGBM refuse ("pandas dtypes must be int, float or bool"). +FLAG_COLUMNS = ["is_working_hours"] + _READING_QUERY = text( """ SELECT @@ -126,6 +130,6 @@ def _typer(frame: pd.DataFrame) -> pd.DataFrame: scoring -- ce n'est pas un effet de bord limite aux colonnes mesurees. """ typee = frame.copy() - for colonne in NUMERIC_COLUMNS: + for colonne in (*NUMERIC_COLUMNS, *FLAG_COLUMNS): typee[colonne] = pd.to_numeric(typee[colonne], errors="coerce") return typee