bugfix
This commit is contained in:
@@ -802,93 +802,63 @@ def extract_numeric_value(raw_value, is_umsatz=False):
|
||||
# Extrahiert und normalisiert Zahlenwerte fuer Vergleichslogik.
|
||||
# Nutzt globale Helfer: clean_text, re.
|
||||
def get_numeric_filter_value(value_str, is_umsatz=False):
|
||||
"""
|
||||
Extrahiert und normalisiert Zahlenwerte fuer die Filterlogik (Umsatz in Mio, Mitarbeiter int).
|
||||
Gibt 0.0 (fuer Umsatz) oder 0 (fuer Mitarbeiter) zurueck, wenn der Wert leer, k.A., nicht numerisch ist, oder 0 ergibt.
|
||||
Beachtet Einheiten (Tsd, Mio, Mrd) fuer Umsatz.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
logger = logging.getLogger(__name__)
|
||||
if value_str is None or pd.isna(value_str) or str(value_str).strip() == '':
|
||||
# Gibt 0 (int/float) zurueck, nicht "k.A." fuer Filterlogik
|
||||
return 0.0 if is_umsatz else 0
|
||||
return 0.0 if is_umsatz else 0 # Für Filterlogik 0, wenn unbekannt
|
||||
|
||||
raw_value_str = str(value_str).strip()
|
||||
# Pruefe auf bekannte "keine Angabe" Strings
|
||||
if raw_value_str.lower() in ['k.a.', 'n/a', '-']:
|
||||
if raw_value_str.lower() in ['k.a.', 'n/a', '-','0','0.0','0,00','0,000']: # "0" als expliziter String auch als "unbekannt" für Filter
|
||||
return 0.0 if is_umsatz else 0
|
||||
|
||||
|
||||
try:
|
||||
processed_value = clean_text(raw_value_str)
|
||||
if processed_value == "k.A." or processed_value.lower() in ['k.a.', 'n/a', '-']:
|
||||
return 0.0 if is_umsatz else 0 # Pruefe erneut nach clean_text
|
||||
if processed_value.lower() in ['k.a.', 'n/a', '-']: return 0.0 if is_umsatz else 0
|
||||
|
||||
|
||||
# Entferne gaengige Praefixe/Suffixe und Spannen-Trennzeichen
|
||||
processed_value = re.sub(r'(?i)^\s*(ca\.?|circa|rund|etwa|ueber|unter|mehr als|weniger als|bis zu)\s+', '', processed_value) # Beruecksichtige Umlaute (ue)
|
||||
processed_value = re.sub(r'(?i)^\s*(ca\.?|circa|rund|etwa|ueber|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() # Nimm nur den ersten Teil bei Spannen
|
||||
|
||||
# Entferne Tausendertrenner (Punkt, Apostroph) und ersetze Komma durch Punkt fuer Dezimal
|
||||
processed_value_no_thousands = processed_value.replace('.', '').replace("'", "")
|
||||
processed_value_final = processed_value_no_thousands.replace(',', '.')
|
||||
|
||||
|
||||
# Finde die erste Sequenz von Ziffern und Punkten (die Zahl)
|
||||
match = re.search(r'([\d.]+)', processed_value_final)
|
||||
if not match:
|
||||
# Wenn nach der Bereinigung keine Ziffern oder Punkte gefunden werden
|
||||
# logger.debug(f"get_numeric_filter_value: Keine numerischen Zeichen gefunden nach Bereinigung von: '{raw_value_str}'") # Zu viel Laerm im Debug
|
||||
return 0.0 if is_umsatz else 0
|
||||
processed_value = re.split(r'\s*(-|–|bis)\s*', processed_value, 1)[0].strip()
|
||||
|
||||
# Tausendertrenner-Logik (vereinfacht, muss ggf. robuster werden)
|
||||
temp_val_for_num = processed_value.replace("'", "")
|
||||
if '.' in temp_val_for_num and ',' in temp_val_for_num:
|
||||
if temp_val_for_num.rfind('.') > temp_val_for_num.rfind(','):
|
||||
temp_val_for_num = temp_val_for_num.replace(',', '')
|
||||
else:
|
||||
temp_val_for_num = temp_val_for_num.replace('.', '').replace(',', '.')
|
||||
elif ',' in temp_val_for_num:
|
||||
temp_val_for_num = temp_val_for_num.replace(',', '.')
|
||||
elif temp_val_for_num.count('.') > 1: # Mehrere Punkte -> Tausendertrenner
|
||||
temp_val_for_num = temp_val_for_num.replace('.', '')
|
||||
|
||||
match = re.search(r'([\d.\-]+)', temp_val_for_num) # Erlaube auch negative Zahlen am Anfang
|
||||
if not match: return 0.0 if is_umsatz else 0
|
||||
|
||||
num_str = match.group(1)
|
||||
try:
|
||||
# Zusaetzliche Pruefungen auf ungueltige Zahlenstrings
|
||||
if not num_str or num_str == '.' or num_str.count('.') > 1:
|
||||
raise ValueError("Leerer oder ungueltiger Zahlenstring gefunden nach Regex Match")
|
||||
# Konvertiere zu Float
|
||||
num = float(num_str)
|
||||
except ValueError as e:
|
||||
# Wenn die Konvertierung zu Float fehlschlaegt
|
||||
# logger.debug(f"Fehler bei Float-Umwandlung des extrahierten Strings '{num_str}' (aus '{raw_value_str}'): {e}") # Zu viel Laerm im Debug
|
||||
return 0.0 if is_umsatz else 0
|
||||
|
||||
|
||||
# --- Einheiten-Skalierung basierend auf ORIGINALSTRING ---
|
||||
# Ziel: Den Wert in die Einheit des Schwellenwerts konvertieren (Mio fuer Umsatz, Integer fuer MA).
|
||||
original_lower = raw_value_str.lower(); multiplier = 1.0
|
||||
|
||||
if is_umsatz: # Umsatz (Schwellenwert in Mio)
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
num = num * 1000.0 # Konvertiere von Mrd zu Mio
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
num = num / 1000.0 # Konvertiere von Tsd zu Mio
|
||||
# Wenn "Mio" oder keine Einheit, nehme num direkt (wird als Mio interpretiert)
|
||||
|
||||
else: # Mitarbeiterzahl (Schwellenwert ist Integer)
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
num = num * 1000000000.0 # Konvertiere von Mrd zu Integer
|
||||
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower): # Beruecksichtige "mill."
|
||||
num = num * 1000000.0 # Konvertiere von Mio zu Integer
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
num = num * 1000.0 # Konvertiere von Tsd zu Integer
|
||||
# Wenn keine Einheit, nehme num direkt (wird als Integer interpretiert)
|
||||
|
||||
# Das Ergebnis muss 0 oder positiv sein fuer die Filterlogik
|
||||
result_num = num if num > 0 else 0 # Werte <= 0 zaehlen nicht
|
||||
if not num_str or num_str == '.' or (num_str.count('.') > 1 and not num_str.endswith('.')):
|
||||
raise ValueError("Ungültiger Zahlenstring")
|
||||
|
||||
num = float(num_str)
|
||||
|
||||
# Einheiten-Skalierung:
|
||||
# WENN is_umsatz=True, gehen wir davon aus, der Wert im Sheet ist BEREITS in Mio,
|
||||
# AUSSER es steht explizit Mrd oder Tsd dabei.
|
||||
original_lower = raw_value_str.lower()
|
||||
|
||||
if is_umsatz:
|
||||
# Rueckgabe als Wert in Millionen (Float)
|
||||
return result_num / 1000000.0
|
||||
else: # Mitarbeiterzahl
|
||||
# Rueckgabe als ganze Zahl
|
||||
return round(result_num)
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
num = num * 1000.0 # von Mrd zu Mio
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
num = num / 1000.0 # von Tsd zu Mio
|
||||
# ANSONSTEN (keine Einheit oder "Mio" explizit): num ist bereits in Mio
|
||||
else: # Mitarbeiter
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower): num = num * 1000000000.0
|
||||
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower): num = num * 1000000.0
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower): num = num * 1000.0
|
||||
|
||||
return num if num > 0 else (0.0 if is_umsatz else 0) # Nur positive Werte, sonst 0 für Filter
|
||||
|
||||
except Exception as e:
|
||||
# Fange unerwartete Fehler ab und logge sie
|
||||
logger.debug(f"Fehler in get_numeric_filter_value fuer Wert '{raw_value_str[:50]}...': {e}")
|
||||
# Rueckgabe 0 bei Fehler fuer Filterlogik
|
||||
logger.debug(f"Fehler in get_numeric_filter_value für Wert '{raw_value_str[:50]}...': {e}")
|
||||
return 0.0 if is_umsatz else 0
|
||||
|
||||
|
||||
@@ -7906,65 +7876,69 @@ class DataProcessor:
|
||||
|
||||
|
||||
def _get_numeric_value_for_plausi(self, value_str, is_umsatz=False):
|
||||
if value_str is None or pd.isna(value_str):
|
||||
return np.nan
|
||||
|
||||
if value_str is None or pd.isna(value_str): return np.nan
|
||||
raw_value_str_clean = str(value_str).strip()
|
||||
|
||||
# Fall 1: Explizit "unbekannt" oder leer
|
||||
if raw_value_str_clean.lower() in ['', 'k.a.', 'n/a', '-']:
|
||||
self.logger.debug(f"_get_numeric_value_for_plausi: Input '{raw_value_str_clean}' als 'unbekannt/leer' (NaN) interpretiert.")
|
||||
if raw_value_str_clean.lower() in ['', 'k.a.', 'n/a', '-', '0', '0.0', '0,00', '0.000']:
|
||||
self.logger.debug(f"_get_numeric_value_for_plausi: Input '{raw_value_str_clean}' als 'unbekannt' (NaN) interpretiert.")
|
||||
return np.nan
|
||||
|
||||
# Fall 2: Versuche, als Zahl zu interpretieren
|
||||
# (Hier sollte die volle Logik von extract_numeric_value für Einheiten etc. stehen)
|
||||
# Für dieses Beispiel eine vereinfachte Version, die Tausendertrenner und Komma als Dezimal behandelt:
|
||||
|
||||
temp_val = clean_text(raw_value_str_clean) # Bereinigen
|
||||
temp_val = clean_text(raw_value_str_clean)
|
||||
if temp_val.lower() in ['k.a.', 'n/a', '-']: return np.nan
|
||||
|
||||
# Entferne Nicht-Zahlen-Zeichen außer Punkt und Komma und Minus am Anfang
|
||||
temp_val = re.sub(r'[^\d.,\-]', '', temp_val)
|
||||
|
||||
# Wenn nach Bereinigung leer
|
||||
if not temp_val: return np.nan
|
||||
|
||||
# Behandlung von Tausenderpunkten und Dezimalkomma
|
||||
if '.' in temp_val and ',' in temp_val: # z.B. 1.234,56 oder 1,234.56
|
||||
if temp_val.rfind('.') > temp_val.rfind(','): # US-Stil: 1,234.56
|
||||
temp_val = temp_val.replace(',', '')
|
||||
else: # EU-Stil: 1.234,56
|
||||
temp_val = temp_val.replace('.', '').replace(',', '.')
|
||||
elif ',' in temp_val: # Nur Komma: 1234,56 -> 1234.56
|
||||
temp_val = temp_val.replace(',', '.')
|
||||
# Wenn nur Punkte: 1.234.567 -> 1234567 (Annahme: Tausendertrenner)
|
||||
# Dies ist heikel, wenn es sich um IP-Adressen oder Versionsnummern handelt.
|
||||
# Aber für Finanzdaten ist es oft so.
|
||||
elif temp_val.count('.') > 1:
|
||||
temp_val = temp_val.replace('.', '')
|
||||
temp_val = re.sub(r'(?i)^\s*(ca\.?|circa|...)\s+', '', temp_val) # Gekürzt für Lesbarkeit
|
||||
temp_val = re.sub(r'[€$£¥]', '', temp_val).strip()
|
||||
temp_val = re.split(r'\s*(-|–|bis)\s*', temp_val, 1)[0].strip()
|
||||
|
||||
# Tausendertrenner-Logik (wie oben)
|
||||
temp_val_for_num = temp_val.replace("'", "")
|
||||
if '.' in temp_val_for_num and ',' in temp_val_for_num:
|
||||
if temp_val_for_num.rfind('.') > temp_val_for_num.rfind(','): temp_val_for_num = temp_val_for_num.replace(',', '')
|
||||
else: temp_val_for_num = temp_val_for_num.replace('.', '').replace(',', '.')
|
||||
elif ',' in temp_val_for_num: temp_val_for_num = temp_val_for_num.replace(',', '.')
|
||||
elif temp_val_for_num.count('.') > 1: temp_val_for_num = temp_val_for_num.replace('.', '')
|
||||
|
||||
match = re.search(r'([\d.\-]+)', temp_val_for_num)
|
||||
if not match:
|
||||
self.logger.debug(f"_get_numeric_value_for_plausi: Kein numerischer Match in '{temp_val_for_num}' (von '{raw_value_str_clean}')")
|
||||
return np.nan
|
||||
|
||||
num_str = match.group(1)
|
||||
try:
|
||||
num = float(temp_val)
|
||||
|
||||
# Einheiten-Skalierung (vereinfacht aus extract_numeric_value)
|
||||
if not num_str or num_str == '.' or (num_str.count('.') > 1 and not num_str.endswith('.')):
|
||||
raise ValueError("Ungültiger Zahlenstring")
|
||||
num = float(num_str) # Das ist die Zahl, wie sie im String steht (ggf. schon Mio)
|
||||
|
||||
original_lower = raw_value_str_clean.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
|
||||
|
||||
final_num = num * multiplier
|
||||
final_num_absolute = num # Initialannahme: Zahl ist in der Grundeinheit (außer wenn Umsatz und keine Einheit)
|
||||
|
||||
# Gemäß Ihrer Regel: Wenn der *ursprüngliche String* "0" (oder Ähnliches) war, soll es als "unbekannt" (NaN) gelten.
|
||||
if final_num == 0.0 and raw_value_str_clean.strip() in ['0', '0.0', '0,0', '0.00', '0,000', '0.000']:
|
||||
self.logger.debug(f"_get_numeric_value_for_plausi: Input '{raw_value_str_clean}' ist explizit '0', als 'unbekannt' (NaN) interpretiert.")
|
||||
return np.nan
|
||||
if is_umsatz:
|
||||
# WENN der String NUR eine Zahl ist (z.B. "173"), NEHMEN WIR AN, ES SIND BEREITS MILLIONEN
|
||||
# und wir wollen den absoluten Wert in Euro für die Plausi-Checks.
|
||||
if not any(kw in original_lower for kw in ['mrd', 'milliarden', 'billion', 'tsd', 'tausend']):
|
||||
# Keine explizite Einheit gefunden, und es ist Umsatz -> interpretiere als Mio
|
||||
final_num_absolute = num * 1000000.0
|
||||
elif re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
final_num_absolute = num * 1000000000.0
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
final_num_absolute = num * 1000.0
|
||||
# Wenn "mio" oder "millionen" explizit drinsteht, ist `num` bereits der Mio-Wert,
|
||||
# also multiplizieren wir mit 1 Mio, um auf Euro zu kommen.
|
||||
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower):
|
||||
final_num_absolute = num * 1000000.0
|
||||
# Sonst (falls es eine andere, nicht behandelte Einheit war), bleibt es `num`
|
||||
# Dies sollte aber durch die erste Bedingung (nur Zahl -> Mio) abgedeckt sein.
|
||||
else: # Mitarbeiter (bereits absolute Zahl erwartet, außer bei expliziten Einheiten)
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower): final_num_absolute = num * 1000000000.0
|
||||
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower): final_num_absolute = num * 1000000.0
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower): final_num_absolute = num * 1000.0
|
||||
|
||||
# self.logger.debug(f"_get_numeric_value_for_plausi: Input '{raw_value_str_clean}' erfolgreich zu {final_num} konvertiert.")
|
||||
return final_num # Gibt die Zahl zurück, auch wenn es 0.0 ist (aus z.B. "0 Tsd")
|
||||
# Ihre Regel: String "0" wurde oben zu NaN.
|
||||
# Wenn final_num_absolute jetzt 0 ist, war es eine Berechnung wie "0 Tsd".
|
||||
return final_num_absolute
|
||||
|
||||
except ValueError:
|
||||
self.logger.debug(f"_get_numeric_value_for_plausi: Konnte '{raw_value_str_clean}' (verarbeitet als '{temp_val}') nicht zu float konvertieren.")
|
||||
except ValueError as e:
|
||||
self.logger.debug(f"_get_numeric_value_for_plausi: ValueError bei Konvertierung von '{num_str}' (von '{raw_value_str_clean}'): {e}")
|
||||
return np.nan
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user