Spaces:
Sleeping
Sleeping
| 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") |