Home_Credit_API / Monitoring /analyse_drift.py
Diaure's picture
CD: update from GitHub main
ceb3888 verified
Raw
History Blame Contribute Delete
5.89 kB
import pandas as pd
import joblib
from evidently.report import Report
from evidently.metrics import (
DatasetSummaryMetric,
DatasetMissingValuesMetric,
ColumnSummaryMetric,
DatasetDriftMetric,
ColumnDriftMetric,
)
from evidently.metrics import ColumnDriftMetric
# =========================
# Chargement des données
# =========================
pipe = joblib.load("./BestModel/pipeline_complet.joblib")
df_example = joblib.load("./data/app_test_clean_v2.joblib")
expected_cols = pipe.feature_names_in_
# df_reference = df_example[expected_cols].copy()
X_ref = pipe[:-1].transform(df_example)
df_reference = pd.DataFrame(X_ref, columns=pipe[:-1].get_feature_names_out())
print(df_reference.shape)
print(df_reference.columns[:20])
df_production = pd.read_parquet("logs/predictions_log.parquet")
print(df_production.shape)
print(df_production.columns[:20])
print(len(set(df_reference.columns).intersection(df_production.columns)))
# =========================
# Colonnes communes
# =========================
df_reference.columns = df_reference.columns.astype(str)
df_production.columns = df_production.columns.astype(str)
common_cols = sorted(
set(df_reference.columns)
.intersection(df_production.columns))
for col in common_cols:
ref_num = pd.api.types.is_numeric_dtype(df_reference[col])
prod_num = pd.api.types.is_numeric_dtype(df_production[col])
if ref_num and prod_num:
df_reference[col] = pd.to_numeric(
df_reference[col],
errors="coerce"
)
df_production[col] = pd.to_numeric(
df_production[col],
errors="coerce"
)
else:
df_reference[col] = (
df_reference[col]
.fillna("MISSING")
.astype(str))
df_production[col] = (
df_production[col]
.fillna("MISSING")
.astype(str))
for col in common_cols:
if not pd.api.types.is_numeric_dtype(df_reference[col]):
assert (
df_reference[col]
.dropna()
.map(type)
.eq(str)
.all()
)
assert (
df_production[col]
.dropna()
.map(type)
.eq(str)
.all()
)
df_reference = df_reference[common_cols].copy()
df_production = df_production[common_cols].copy()
# Conversion bool en int
for col in common_cols:
if df_reference[col].dtype == bool:
df_reference[col] = df_reference[col].astype(int)
if df_production[col].dtype == bool:
df_production[col] = df_production[col].astype(int)
# Harmonisation robuste
for col in common_cols:
ref_types = set(df_reference[col].dropna().map(type))
prod_types = set(df_production[col].dropna().map(type))
all_types = ref_types.union(prod_types)
ref_num = pd.api.types.is_numeric_dtype(df_reference[col])
prod_num = pd.api.types.is_numeric_dtype(df_production[col])
# Mélange de types Python
if len(all_types) > 1:
print(f"Conversion forcée en str : {col}")
df_reference[col] = (df_reference[col].fillna("MISSING").astype(str))
df_production[col] = (df_production[col].fillna("MISSING").astype(str))
# Numérique des deux côtés
elif ref_num and prod_num:
df_reference[col] = pd.to_numeric(df_reference[col], errors="coerce")
df_production[col] = pd.to_numeric(df_production[col], errors="coerce")
# Catégoriel
else:
df_reference[col] = (df_reference[col].fillna("MISSING").astype(str))
df_production[col] = (df_production[col].fillna("MISSING").astype(str))
# Colonnes vides
valid_cols = []
for col in common_cols:
if(df_reference[col].notna().sum() > 0 and df_production[col].notna().sum() > 0):
valid_cols.append(col)
df_reference = df_reference[valid_cols]
df_production = df_production[valid_cols]
# Colonnes constantes
non_constant_cols = []
for col in valid_cols:
if (df_reference[col].nunique(dropna=True) > 1 and df_production[col].nunique(dropna=True) > 1):
non_constant_cols.append(col)
df_reference = df_reference[non_constant_cols]
df_production = df_production[non_constant_cols]
# Vérification finale
print("\n=== TYPES FINAUX ===")
for col in df_reference.columns:
if df_reference[col].dtype != df_production[col].dtype:
print(
f"Mismatch : {col} -> "
f"{df_reference[col].dtype} / "
f"{df_production[col].dtype}")
print("\nNb colonnes finales :", len(df_reference.columns))
print(df_reference.shape)
print(df_production.shape)
print(len(set(df_reference.columns).intersection(df_production.columns)))
print(df_reference.columns[:20].tolist())
print(df_production.columns[:20].tolist())
print(set(df_reference.columns) - set(df_production.columns))
print(df_production.describe().T)
df_production.nunique().sort_values()
print(df_reference.columns.tolist()[:50])
# Rapport Evidently
metrics = [
DatasetSummaryMetric(),
DatasetMissingValuesMetric(),
DatasetDriftMetric(),]
for col in df_reference.columns:
metrics.append(ColumnSummaryMetric(column_name=col))
metrics.append(ColumnDriftMetric(column_name=col))
print("\n=== RECHERCHE COLONNE PROBLEMATIQUE ===")
for col in df_reference.columns:
ref_types = set(type(x) for x in df_reference[col].dropna().unique())
prod_types = set(type(x) for x in df_production[col].dropna().unique())
if len(ref_types.union(prod_types)) > 1:
print(
f"{col}\n"
f" REF={ref_types}\n"
f" PROD={prod_types}\n")
report = Report(metrics=metrics)
report.run(reference_data=df_reference, current_data=df_production,)
report.save_html("Monitoring/drift_report.html")
report.save_json("Monitoring/drift_report.json")
print("\nRapport généré : "
"Monitoring/drift_report.json")