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`.
This commit is contained in:
Johan LEROY
2026-09-22 14:10:35 +02:00
parent fb4235df3a
commit 5b5c97532d
+5 -1
View File
@@ -43,6 +43,10 @@ NUMERIC_COLUMNS = [
"capacity_kw", "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( _READING_QUERY = text(
""" """
SELECT 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. scoring -- ce n'est pas un effet de bord limite aux colonnes mesurees.
""" """
typee = frame.copy() typee = frame.copy()
for colonne in NUMERIC_COLUMNS: for colonne in (*NUMERIC_COLUMNS, *FLAG_COLUMNS):
typee[colonne] = pd.to_numeric(typee[colonne], errors="coerce") typee[colonne] = pd.to_numeric(typee[colonne], errors="coerce")
return typee return typee