diff --git a/brancheneinstufung.py b/brancheneinstufung.py index a2df56dc8..d7bebbfa7 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -54,6 +54,7 @@ import unicodedata # Fuer Text-Normalisierung # Bibliotheken fuer Datenanalyse und ML import pandas as pd import numpy as np +from imblearn.over_sampling import SMOTE from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.impute import SimpleImputer @@ -8942,7 +8943,7 @@ class DataProcessor: # und weisen ihnen interne, einfachere Namen zu, die im DataFrame verwendet werden. col_keys_mapping = { "name": "CRM Name", # Zur Identifikation, wird spaeter entfernt - "branche_crm": "CRM Branche", # Fuer One-Hot Encoding + "branche_ki": "Chat Vorschlag Branche", # Fuer One-Hot Encoding "umsatz_crm": "CRM Umsatz", # Fuer Konsolidierung "umsatz_wiki": "Wiki Umsatz", # Fuer Konsolidierung "ma_crm": "CRM Anzahl Mitarbeiter", # Fuer Konsolidierung @@ -9098,8 +9099,8 @@ class DataProcessor: # --- Kategoriale Features vorbereiten (Branche) --- self.logger.info("Verarbeite kategoriales Feature 'branche_crm' fuer One-Hot Encoding...") - branche_col_internal = "branche_crm" - df_filtered.loc[:, 'branche_crm'] = df_filtered['branche_crm'].astype(str).fillna('Unbekannt').str.strip() + branche_col_internal = "branche_ki" + df_filtered.loc[:, 'branche_ki'] = df_filtered['branche_crm'].astype(str).fillna('Unbekannt').str.strip() df_encoded = pd.get_dummies(df_filtered, columns=[branche_col_internal], prefix='Branche', dummy_na=False) # --- Finale Auswahl der Features fuer das Modell --- @@ -9206,6 +9207,22 @@ class DataProcessor: X_test_imputed = imputer.transform(X_test) X_train_imputed = pd.DataFrame(X_train_imputed, columns=feature_columns_ml) # feature_columns_ml verwenden X_test_imputed = pd.DataFrame(X_test_imputed, columns=feature_columns_ml) # feature_columns_ml verwenden + + # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + # +++ NEU: Klassen-Balancierung mit SMOTE auf den Trainingsdaten ++++++++ + # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + self.logger.info("Führe Klassen-Balancierung mit SMOTE auf den Trainingsdaten durch...") + self.logger.info(f"Klassenverteilung VOR SMOTE:\n{y_train.value_counts()}") + + smote = SMOTE(random_state=42) + # Wichtig: SMOTE wird auf die imputierten Trainingsdaten angewendet + X_train_resampled, y_train_resampled = smote.fit_resample(X_train_imputed, y_train) + + self.logger.info(f"Klassenverteilung NACH SMOTE:\n{y_train_resampled.value_counts()}") + self.logger.info(f"Größe des Trainingssets nach Resampling: {len(X_train_resampled)} Zeilen.") + # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + # +++ ENDE SMOTE-BLOCK ++++++++++++++++++++++++++++++++++++++++++++++++++ + # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # +++ ANPASSUNG HIER: RandomForest statt Decision Tree ++++++++++++++++++