bugfix
This commit is contained in:
@@ -5563,58 +5563,46 @@ class DataProcessor:
|
||||
return np.nan
|
||||
raw_value_str = str(value_str)
|
||||
try:
|
||||
# Kopieren Sie hier die Logik von extract_numeric_value, die NaN zurückgibt
|
||||
# anstatt "k.A." bei Fehlern oder 0/negativen Werten.
|
||||
processed_value = clean_text(raw_value_str) # Annahme: clean_text existiert
|
||||
if processed_value == "k.A.":
|
||||
return np.nan
|
||||
# Kopieren Sie hier die Logik von extract_numeric_value, die NaN zurückgibt
|
||||
# anstatt "k.A." bei Fehlern oder 0/negativen Werten.
|
||||
processed_value = clean_text(raw_value_str) # Annahme: clean_text existiert
|
||||
if processed_value == "k.A.":
|
||||
return np.nan
|
||||
|
||||
processed_value = re.sub(
|
||||
r'(?i)^\s*(ca\.?|circa|rund|etwa|über|unter|mehr als|weniger als|bis zu)\s+',
|
||||
'',
|
||||
processed_value
|
||||
)
|
||||
processed_value = re.sub(r'[€$£¥]', '', processed_value).strip()
|
||||
processed_value = re.split(
|
||||
r'\s*(-|–|bis)\s*',
|
||||
processed_value,
|
||||
1
|
||||
)[0].strip()
|
||||
processed_value_no_thousands = processed_value.replace('.', '').replace("'", "")
|
||||
processed_value_final = processed_value_no_thousands.replace(',', '.')
|
||||
processed_value = re.sub(r'(?i)^\s*(ca\.?|circa|rund|etwa|über|unter|mehr als|weniger als|bis zu)\s+', '', processed_value)
|
||||
processed_value = re.sub(r'[€$£¥]', '', processed_value).strip()
|
||||
processed_value = re.split(r'\s*(-|–|bis)\s*', processed_value, 1)[0].strip()
|
||||
processed_value_no_thousands = processed_value.replace('.', '').replace("'", "")
|
||||
processed_value_final = processed_value_no_thousands.replace(',', '.')
|
||||
|
||||
match = re.search(r'([\d.]+)', processed_value_final)
|
||||
if not match:
|
||||
return np.nan
|
||||
match = re.search(r'([\d.]+)', processed_value_final)
|
||||
if not match:
|
||||
return np.nan
|
||||
|
||||
num_str = match.group(1)
|
||||
if not num_str or num_str == '.':
|
||||
return np.nan
|
||||
num_str = match.group(1)
|
||||
if not num_str or num_str == '.':
|
||||
return np.nan
|
||||
|
||||
num = float(num_str)
|
||||
num = float(num_str)
|
||||
|
||||
original_lower = raw_value_str.lower()
|
||||
multiplier = 1.0
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
multiplier = 1000000000.0
|
||||
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill\.\s*\b', original_lower):
|
||||
multiplier = 1000000.0
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
multiplier = 1000.0
|
||||
original_lower = raw_value_str.lower()
|
||||
multiplier = 1.0
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
multiplier = 1000000000.0
|
||||
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill\.\s*\b', original_lower):
|
||||
multiplier = 1000000.0
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
multiplier = 1000.0
|
||||
|
||||
num = num * multiplier
|
||||
return num if num > 0 else np.nan # Nur positive Werte zählen
|
||||
num = num * multiplier
|
||||
|
||||
return num if num > 0 else np.nan # Nur positive Werte zählen
|
||||
|
||||
except (ValueError, TypeError) as e:
|
||||
logging.debug(
|
||||
f"Konntze Wert '{str(value_str)[:50]}...' nicht als gültige Zahl parsen: {e}"
|
||||
)
|
||||
logging.debug(f"Konntze Wert '{str(value_str)[:50]}...' nicht als gültige Zahl parsen: {e}")
|
||||
return np.nan
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
f"Unerwarteter Fehler in get_valid_numeric für Wert "
|
||||
f"'{str(value_str)[:50]}...': {e}"
|
||||
)
|
||||
logging.warning(f"Unerwarteter Fehler in get_valid_numeric für Wert '{str(value_str)[:50]}...': {e}")
|
||||
return np.nan
|
||||
|
||||
|
||||
@@ -5624,20 +5612,10 @@ class DataProcessor:
|
||||
}
|
||||
|
||||
for base_name, (wiki_col, crm_col, final_col) in cols_to_process.items():
|
||||
logging.info(
|
||||
f"Verarbeite und konsolidiere '{base_name}' (Priorität: Wiki > CRM)..."
|
||||
)
|
||||
|
||||
logging.info(f"Verarbeite und konsolidiere '{base_name}' (Priorität: Wiki > CRM)...")
|
||||
# Sicherstellen, dass Spalten existieren (get_valid_numeric behandelt None)
|
||||
if wiki_col in df_subset.columns:
|
||||
wiki_series = df_subset[wiki_col].apply(get_valid_numeric)
|
||||
else:
|
||||
wiki_series = pd.Series(np.nan, index=df_subset.index)
|
||||
|
||||
if crm_col in df_subset.columns:
|
||||
crm_series = df_subset[crm_col].apply(get_valid_numeric)
|
||||
else:
|
||||
crm_series = pd.Series(np.nan, index=df_subset.index)
|
||||
wiki_series = df_subset[wiki_col].apply(get_valid_numeric) if wiki_col in df_subset.columns else pd.Series(np.nan, index=df_subset.index)
|
||||
crm_series = df_subset[crm_col].apply(get_valid_numeric) if crm_col in df_subset.columns else pd.Series(np.nan, index=df_subset.index)
|
||||
|
||||
# np.where wählt den ersten Wert, wenn er nicht NaN ist, sonst den zweiten
|
||||
df_subset[final_col] = np.where(
|
||||
@@ -5645,16 +5623,14 @@ class DataProcessor:
|
||||
wiki_series,
|
||||
crm_series
|
||||
)
|
||||
|
||||
logging.info(
|
||||
f" -> {df_subset[final_col].notna().sum()} gültige "
|
||||
f"'{final_col}' Werte erstellt (von {len(df_subset)} Zeilen)."
|
||||
)
|
||||
logging.info(f" -> {df_subset[final_col].notna().sum()} gültige '{final_col}' Werte erstellt (von {len(df_subset)} Zeilen).")
|
||||
|
||||
# --- Zielvariable vorbereiten (Technikerzahl) ---
|
||||
techniker_col = "techniker" # Interne Spaltenname nach Umbenennung
|
||||
techniker_col = "techniker" # Interne Spaltenname nach Umbenennung
|
||||
logging.info(f"Verarbeite Zielvariable '{techniker_col}'...")
|
||||
if techniker_col not in df_subset.columns: logging.critical(f"FEHLER: Zielvariable '{techniker_col}' fehlt."); return None
|
||||
if techniker_col not in df_subset.columns:
|
||||
logging.critical(f"FEHLER: Zielvariable '{techniker_col}' fehlt.")
|
||||
return None
|
||||
df_subset['Anzahl_Servicetechniker_Numeric'] = pd.to_numeric(df_subset[techniker_col], errors='coerce')
|
||||
|
||||
# Filtere Zeilen: Behalte nur die mit gültiger, positiver Technikerzahl (> 0)
|
||||
@@ -5663,25 +5639,46 @@ class DataProcessor:
|
||||
df_subset['Anzahl_Servicetechniker_Numeric'].notna() &
|
||||
(df_subset['Anzahl_Servicetechniker_Numeric'] > 0)
|
||||
].copy()
|
||||
filtered_rows = len(df_filtered); removed_rows = initial_rows - filtered_rows;
|
||||
if removed_rows > 0: logging.info(f"{removed_rows} Zeilen entfernt (fehlende/ungültige Technikerzahl).")
|
||||
filtered_rows = len(df_filtered)
|
||||
removed_rows = initial_rows - filtered_rows
|
||||
if removed_rows > 0:
|
||||
logging.info(f"{removed_rows} Zeilen entfernt (fehlende/ungültige Technikerzahl).")
|
||||
logging.info(f"Verbleibende Zeilen für Modellierung (mit gültiger Technikerzahl > 0): {filtered_rows}")
|
||||
if filtered_rows == 0: logging.error("FEHLER: Keine Zeilen mit gültiger Technikerzahl (>0) übrig!"); return None
|
||||
if filtered_rows == 0:
|
||||
logging.error("FEHLER: Keine Zeilen mit gültiger Technikerzahl (>0) übrig!")
|
||||
return None
|
||||
|
||||
# --- Techniker-Buckets erstellen ---
|
||||
# Bins und Labels wie definiert ([-1, 0, 19, 49, 99, 249, 499, float('inf')])
|
||||
bins = [-1, 0, 19, 49, 99, 249, 499, float('inf')]
|
||||
labels = ['Bucket_1_(0)', 'Bucket_2_(<20)', 'Bucket_3_(<50)', 'Bucket_4_(<100)', 'Bucket_5_(<250)', 'Bucket_6_(<500)', 'Bucket_7_(>499)']
|
||||
df_filtered['Techniker_Bucket'] = pd.cut( df_filtered['Anzahl_Servicetechniker_Numeric'], bins=bins, labels=labels, right=True, include_lowest=True )
|
||||
labels = [
|
||||
'Bucket_1_(0)', 'Bucket_2_(<20)', 'Bucket_3_(<50)',
|
||||
'Bucket_4_(<100)', 'Bucket_5_(<250)', 'Bucket_6_(<500)', 'Bucket_7_(>499)'
|
||||
]
|
||||
df_filtered['Techniker_Bucket'] = pd.cut(
|
||||
df_filtered['Anzahl_Servicetechniker_Numeric'],
|
||||
bins=bins,
|
||||
labels=labels,
|
||||
right=True,
|
||||
include_lowest=True
|
||||
)
|
||||
logging.info("Techniker-Buckets erstellt.")
|
||||
logging.info(f"Verteilung der Techniker-Buckets im Trainingsdatensatz:\n{df_filtered['Techniker_Bucket'].value_counts(normalize=True).round(3)}")
|
||||
if df_filtered['Techniker_Bucket'].isna().any(): logging.warning("WARNUNG: NaNs in Techniker-Buckets erstellt. Entferne diese Zeilen."); df_filtered.dropna(subset=['Techniker_Bucket'], inplace=True); logging.info(f"Nach Entfernung von NaN Buckets: {len(df_filtered)} Zeilen verbleiben."); if len(df_filtered) == 0: logging.error("FEHLER: Keine Zeilen übrig nach Entfernung von NaN Buckets."); return None;
|
||||
|
||||
# NaN-Buckets entfernen
|
||||
if df_filtered['Techniker_Bucket'].isna().any():
|
||||
logging.warning("WARNUNG: NaNs in Techniker-Buckets erstellt. Entferne diese Zeilen.")
|
||||
df_filtered.dropna(subset=['Techniker_Bucket'], inplace=True)
|
||||
logging.info(f"Nach Entfernung von NaN Buckets: {len(df_filtered)} Zeilen verbleiben.")
|
||||
if len(df_filtered) == 0:
|
||||
logging.error("FEHLER: Keine Zeilen übrig nach Entfernung von NaN Buckets.")
|
||||
return None
|
||||
|
||||
# --- Kategoriale Features vorbereiten (Branche) ---
|
||||
branche_col = "branche" # Interne Spaltenname
|
||||
branche_col = "branche" # Interne Spaltenname
|
||||
logging.info(f"Verarbeite kategoriales Feature '{branche_col}' für One-Hot Encoding...")
|
||||
if branche_col not in df_filtered.columns: logging.critical(f"FEHLER: Spalte '{branche_col}' fehlt für One-Hot Encoding."); return None;
|
||||
if branche_col not in df_filtered.columns:
|
||||
logging.critical(f"FEHLER: Spalte '{branche_col}' fehlt für One-Hot Encoding.")
|
||||
return None
|
||||
df_filtered[branche_col] = df_filtered[branche_col].astype(str).fillna('Unbekannt').str.strip()
|
||||
df_encoded = pd.get_dummies(df_filtered, columns=[branche_col], prefix='Branche', dummy_na=False)
|
||||
logging.info(f"One-Hot Encoding für '{branche_col}' durchgeführt. Neue Spaltenanzahl: {len(df_encoded.columns)}")
|
||||
@@ -5689,19 +5686,29 @@ class DataProcessor:
|
||||
# --- Finale Auswahl der Features für das Modell ---
|
||||
feature_columns = [col for col in df_encoded.columns if col.startswith('Branche_')]
|
||||
feature_columns.extend(['Finaler_Umsatz', 'Finaler_Mitarbeiter'])
|
||||
if not all(col in df_encoded.columns for col in ['Finaler_Umsatz', 'Finaler_Mitarbeiter']): logging.critical("FEHLER: Konsolidierte numerische Spalten fehlen."); return None;
|
||||
target_column = 'Techniker_Bucket'; original_data_cols = ['name', 'Anzahl_Servicetechniker_Numeric'];
|
||||
if not all(col in df_encoded.columns for col in original_data_cols): logging.critical(f"FEHLER: Originaldaten-Spalten {original_data_cols} fehlen."); return None;
|
||||
if not all(col in df_encoded.columns for col in ['Finaler_Umsatz', 'Finaler_Mitarbeiter']):
|
||||
logging.critical("FEHLER: Konsolidierte numerische Spalten fehlen.")
|
||||
return None
|
||||
target_column = 'Techniker_Bucket'
|
||||
original_data_cols = ['name', 'Anzahl_Servicetechniker_Numeric']
|
||||
if not all(col in df_encoded.columns for col in original_data_cols):
|
||||
logging.critical(f"FEHLER: Originaldaten-Spalten {original_data_cols} fehlen.")
|
||||
return None
|
||||
|
||||
df_model_ready = df_encoded[original_data_cols + feature_columns + [target_column]].copy()
|
||||
for col in ['Finaler_Umsatz', 'Finaler_Mitarbeiter', 'Anzahl_Servicetechniker_Numeric']:
|
||||
if col in df_model_ready.columns: df_model_ready[col] = pd.to_numeric(df_model_ready[col], errors='coerce')
|
||||
if col in df_model_ready.columns:
|
||||
df_model_ready[col] = pd.to_numeric(df_model_ready[col], errors='coerce')
|
||||
df_model_ready = df_model_ready.reset_index(drop=True)
|
||||
|
||||
logging.info("Datenvorbereitung für Modellierung abgeschlossen."); logging.info(f"Finaler DataFrame hat {len(df_model_ready)} Zeilen und {len(df_model_ready.columns)} Spalten.");
|
||||
logging.info(f"Anzahl Feature-Spalten: {len(feature_columns)}"); logging.info(f"Ziel-Spalte: {target_column}");
|
||||
nan_counts = df_model_ready[['Finaler_Umsatz', 'Finaler_Mitarbeiter']].isna().sum(); logging.info(f"Fehlende Werte in numerischen Features vor Imputation:\n{nan_counts}");
|
||||
rows_with_nan = df_model_ready[['Finaler_Umsatz', 'Finaler_Mitarbeiter']].isna().any(axis=1).sum(); logging.info(f"Anzahl Zeilen mit mindestens einem fehlenden numerischen Feature: {rows_with_nan}");
|
||||
logging.info("Datenvorbereitung für Modellierung abgeschlossen.")
|
||||
logging.info(f"Finaler DataFrame hat {len(df_model_ready)} Zeilen und {len(df_model_ready.columns)} Spalten.")
|
||||
logging.info(f"Anzahl Feature-Spalten: {len(feature_columns)}")
|
||||
logging.info(f"Ziel-Spalte: {target_column}")
|
||||
nan_counts = df_model_ready[['Finaler_Umsatz', 'Finaler_Mitarbeiter']].isna().sum()
|
||||
logging.info(f"Fehlende Werte in numerischen Features vor Imputation:\n{nan_counts}")
|
||||
rows_with_nan = df_model_ready[['Finaler_Umsatz', 'Finaler_Mitarbeiter']].isna().any(axis=1).sum()
|
||||
logging.info(f"Anzahl Zeilen mit mindestens einem fehlenden numerischen Feature: {rows_with_nan}")
|
||||
|
||||
return df_model_ready
|
||||
|
||||
|
||||
Reference in New Issue
Block a user