diff --git a/data_processor.py b/data_processor.py index d0d14a63..22e9a295 100644 --- a/data_processor.py +++ b/data_processor.py @@ -5504,8 +5504,7 @@ class DataProcessor: 'Techniker_Mittel (50-249)', 'Techniker_Gross (250+)'] - df_filtered.loc[:, - 'Techniker_Bucket'] = pd.cut(df_filtered['Anzahl_Servicetechniker_Numeric'], + df_filtered.loc[:, 'Techniker_Bucket'] = pd.cut(df_filtered['Anzahl_Servicetechniker_Numeric'], bins=bins_new, labels=labels_new, right=True, @@ -5514,7 +5513,7 @@ class DataProcessor: f"Verteilung der neuen Techniker-Buckets:\n{df_filtered['Techniker_Bucket'].value_counts(normalize=True, dropna=False).sort_index().round(3)}") # --- Kategoriale Features vorbereiten (Branchen-Gruppen) --- - # Dies ist die Spalte mit den Detail-Branchen + # Wir verwenden konsistent die KI-generierte Spalte 'branche_ki' als Basis für die Gruppierung. branche_col_internal = "branche_ki" self.logger.info(f"Verarbeite kategoriales Feature '{branche_col_internal}' und mappe es zu 'Branchen_Gruppe'...") @@ -5522,84 +5521,52 @@ class DataProcessor: self.logger.critical(f"FEHLER: Die für das Mapping benötigte Spalte '{branche_col_internal}' wurde nicht im DataFrame gefunden. Breche ab.") return None - # Wende das saubere Mapping aus der Config an. Die Schlüssel sind jetzt lesbar. - # Wir müssen die Eingabedaten aus dem Sheet NICHT MEHR normalisieren. + # Wende das saubere Mapping aus der Config an, um nur den 'gruppe'-String zu extrahieren. branch_group_map = {branch_name: details.get('gruppe', 'Sonstige') for branch_name, details in Config.BRANCH_GROUP_MAPPING.items()} - df_filtered.loc[:, 'Branchen_Gruppe'] = df_filtered[branche_col_internal].map(branch_group_map).fillna('Sonstige') + df_filtered.loc[:, 'Branchen_Gruppe'] = df_filtered[branche_col_internal].map(branch_group_map).fillna('Sonstige') self.logger.info("Mapping zu 'Branchen_Gruppe' durchgeführt.") self.logger.debug( f"Verteilung der Branchen-Gruppen:\n{df_filtered['Branchen_Gruppe'].value_counts(normalize=True).sort_index().round(3)}") - # One-Hot Encoding wird jetzt auf der neuen 'Branchen_Gruppe'-Spalte - # durchgeführt - df_encoded = pd.get_dummies( - df_filtered, - columns=['Branchen_Gruppe'], - prefix='Gruppe', - dummy_na=False) - self.logger.info( - f"One-Hot Encoding fuer 'Branchen_Gruppe' durchgefuehrt.") + # --- One-Hot Encoding --- + df_encoded = pd.get_dummies(df_filtered, columns=['Branchen_Gruppe'], prefix='Gruppe', dummy_na=False) + self.logger.info("One-Hot Encoding fuer 'Branchen_Gruppe' durchgefuehrt.") # --- Finale Auswahl der Features fuer das Modell --- - feature_columns_ml = [ - col for col in df_encoded.columns if col.startswith('Gruppe_')] - feature_columns_ml.extend([ - 'Log_Finaler_Umsatz_ML', - 'Log_Finaler_Mitarbeiter_ML', - 'Umsatz_pro_MA_ML', - 'is_part_of_group' - ]) - self.logger.info( - f"Finale Feature-Auswahl für das Training: {feature_columns_ml}") + feature_columns_ml = [col for col in df_encoded.columns if col.startswith('Gruppe_')] + feature_columns_ml.extend(['Log_Finaler_Umsatz_ML', 'Log_Finaler_Mitarbeiter_ML', 'Umsatz_pro_MA_ML', 'is_part_of_group']) + self.logger.info(f"Finale Feature-Auswahl für das Training: {feature_columns_ml}") target_column_ml = 'Techniker_Bucket' identification_cols_ml = ['name', 'Anzahl_Servicetechniker_Numeric'] - final_cols_for_df_ml = identification_cols_ml + \ - feature_columns_ml + [target_column_ml] - missing_final_cols_ml = [ - col for col in final_cols_for_df_ml if col not in df_encoded.columns] + final_cols_for_df_ml = identification_cols_ml + feature_columns_ml + [target_column_ml] + missing_final_cols_ml = [col for col in final_cols_for_df_ml if col not in df_encoded.columns] if missing_final_cols_ml: - self.logger.critical( - f"FEHLER: Finale Spalten fuer Modellierung fehlen im DataFrame: {missing_final_cols_ml}") + self.logger.critical(f"FEHLER: Finale Spalten fuer Modellierung fehlen im DataFrame: {missing_final_cols_ml}") return None df_model_ready = df_encoded[final_cols_for_df_ml].copy() - numeric_features_to_convert = [ - 'Log_Finaler_Umsatz_ML', - 'Log_Finaler_Mitarbeiter_ML', - 'Umsatz_pro_MA_ML', - 'Anzahl_Servicetechniker_Numeric'] + numeric_features_to_convert = ['Log_Finaler_Umsatz_ML', 'Log_Finaler_Mitarbeiter_ML', 'Umsatz_pro_MA_ML', 'Anzahl_Servicetechniker_Numeric'] for col in numeric_features_to_convert: if col in df_model_ready.columns: - df_model_ready[col] = pd.to_numeric( - df_model_ready[col], errors='coerce') + df_model_ready[col] = pd.to_numeric(df_model_ready[col], errors='coerce') df_model_ready = df_model_ready.reset_index(drop=True) - self.logger.info( - "Datenvorbereitung fuer Modellierung (Training) abgeschlossen.") - self.logger.info( - f"Finaler DataFrame fuer Modellierung hat {len(df_model_ready)} Zeilen und {len(df_model_ready.columns)} Spalten.") + self.logger.info("Datenvorbereitung fuer Modellierung (Training) abgeschlossen.") + self.logger.info(f"Finaler DataFrame fuer Modellierung hat {len(df_model_ready)} Zeilen und {len(df_model_ready.columns)} Spalten.") self.logger.info(f"Anzahl Feature-Spalten: {len(feature_columns_ml)}") - numeric_features_for_imputation_ml = [ - 'Log_Finaler_Umsatz_ML', - 'Log_Finaler_Mitarbeiter_ML', - 'Umsatz_pro_MA_ML' - ] - existing_numeric_features = [ - col for col in numeric_features_for_imputation_ml if col in df_model_ready.columns] + numeric_features_for_imputation_ml = ['Log_Finaler_Umsatz_ML', 'Log_Finaler_Mitarbeiter_ML', 'Umsatz_pro_MA_ML'] + existing_numeric_features = [col for col in numeric_features_for_imputation_ml if col in df_model_ready.columns] if existing_numeric_features: nan_counts = df_model_ready[existing_numeric_features].isna().sum() - self.logger.info( - f"Fehlende Werte in numerischen Features vor Imputation:\n{nan_counts}") - rows_with_nan = df_model_ready[existing_numeric_features].isna().any( - axis=1).sum() - self.logger.info( - f"Anzahl Zeilen mit mindestens einem fehlenden numerischen Feature (vor Imputation): {rows_with_nan}") + self.logger.info(f"Fehlende Werte in numerischen Features vor Imputation:\n{nan_counts}") + rows_with_nan = df_model_ready[existing_numeric_features].isna().any(axis=1).sum() + self.logger.info(f"Anzahl Zeilen mit mindestens einem fehlenden numerischen Feature (vor Imputation): {rows_with_nan}") return df_model_ready