bugfix
This commit is contained in:
@@ -715,92 +715,117 @@ def fuzzy_similarity(str1, str2):
|
||||
# Extrahiert und normalisiert Zahlenwerte aus Strings.
|
||||
# Nutzt globale Helfer: clean_text, re.
|
||||
def extract_numeric_value(raw_value, is_umsatz=False):
|
||||
"""
|
||||
Extrahiert und normalisiert Zahlenwerte (Umsatz in Mio, Mitarbeiter).
|
||||
Berücksichtigt Tausendertrenner (Punkt, Apostroph), Dezimaltrenner (Komma), Einheiten (Tsd, Mio, Mrd)
|
||||
und gaengige Praefixe/Suffixe. Gibt "k.A." zurueck, wenn nicht extrahierbar oder <= 0.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if not raw_value: return "k.A."
|
||||
raw_value_str = str(raw_value).strip()
|
||||
# Pruefe auf bekannte "keine Angabe" Strings oder 0 als Text
|
||||
if not raw_value_str or raw_value_str.lower() in ['k.a.', 'n/a', '-']:
|
||||
return "k.A." # 0 als Text ist hier wie k.A.
|
||||
|
||||
|
||||
# Bereinigungsschritte aehnlich wie in clean_text und vorheriger Implementierung
|
||||
processed_value = clean_text(raw_value_str)
|
||||
if processed_value == "k.A." or processed_value.lower() in ['k.a.', 'n/a', '-']:
|
||||
return "k.A." # Pruefe erneut nach clean_text
|
||||
|
||||
|
||||
# logger.debug(f"extract_numeric_value: Verarbeite Wert: '{raw_value_str}' -> '{processed_value}' (is_umsatz={is_umsatz})") # Zu viel Laerm im Debug
|
||||
|
||||
# Entferne gaengige Praefixe/Suffixe und Spannen-Trennzeichen (z.B. "ca. ", "ueber ", "100-200", "€")
|
||||
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'[€$£¥]', '', processed_value).strip() # Entferne Waehrungssymbole
|
||||
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
|
||||
# Reihenfolge ist wichtig: Erst Punkte/Apostrophe entfernen, dann Komma durch Punkt ersetzen
|
||||
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"extract_numeric_value: Keine numerischen Zeichen gefunden nach Bereinigung von: '{raw_value_str}'")
|
||||
logger = logging.getLogger(__name__ + ".extract_numeric_value") # Logger für diese Funktion
|
||||
if raw_value is None or pd.isna(raw_value):
|
||||
return "k.A."
|
||||
|
||||
raw_value_str_original = str(raw_value).strip()
|
||||
if not raw_value_str_original or raw_value_str_original.lower() in ['k.a.', 'n/a', '-']:
|
||||
return "k.A."
|
||||
|
||||
num_str = match.group(1)
|
||||
# Spezifische Behandlung für "0" gemäß Ihrer Regel: Wenn der Input nur "0" ist,
|
||||
# dann ist es für die *Extraktion* erstmal eine 0, die dann in der
|
||||
# *Konsolidierung* ggf. zu "k.A." wird, wenn keine bessere Quelle da ist.
|
||||
# Für die Plausi-Checks wird String "0" dann als NaN (unbekannt) interpretiert.
|
||||
# Hier lassen wir "0" als numerische 0 durch, wenn es die einzige Zahl ist.
|
||||
|
||||
try:
|
||||
# Zusaetzliche Pruefungen auf ungueltige Zahlenstrings (z.B. nur '.', '..', '.1.')
|
||||
if not num_str or num_str == '.' or num_str.count('.') > 1:
|
||||
# Wenn der String leer ist, nur ein Punkt, oder mehr als ein Dezimalpunkt hat
|
||||
raise ValueError("Leerer oder ungueltiger Zahlenstring gefunden nach Regex Match")
|
||||
# Konvertiere den extrahierten String zu einem 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}")
|
||||
# Schritt 1: Grundlegende Textbereinigung
|
||||
processed_value = clean_text(raw_value_str_original) # Ihre globale clean_text Funktion
|
||||
if processed_value.lower() in ['k.a.', 'n/a', '-']:
|
||||
return "k.A."
|
||||
|
||||
# Schritt 2: Präfixe, Suffixe, Währungssymbole und Spannen entfernen
|
||||
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'[€$£¥ CHF]', '', processed_value, flags=re.IGNORECASE).strip() # CHF hinzugefügt
|
||||
processed_value = re.split(r'\s*(-|–|bis)\s*', processed_value, 1)[0].strip()
|
||||
|
||||
# Schritt 3: Klammerinhalte entfernen (z.B. Jahreszahlen)
|
||||
num_extraction_str = re.sub(r'\(.*?\)', '', processed_value).strip()
|
||||
|
||||
# Schritt 4: Apostrophe und Leerzeichen zwischen Ziffern entfernen
|
||||
num_extraction_str = num_extraction_str.replace("'", "")
|
||||
num_extraction_str = re.sub(r'(?<=\d)\s+(?=\d)', '', num_extraction_str) # Entfernt Leerzeichen nur zwischen Ziffern
|
||||
|
||||
if not num_extraction_str: return "k.A."
|
||||
|
||||
# Schritt 5: Punkte und Kommas als Tausender-/Dezimaltrennzeichen standardisieren
|
||||
has_dot = '.' in num_extraction_str
|
||||
has_comma = ',' in num_extraction_str
|
||||
|
||||
if has_dot and has_comma:
|
||||
if num_extraction_str.rfind('.') > num_extraction_str.rfind(','): # US-Stil: 1,234.56
|
||||
num_extraction_str = num_extraction_str.replace(',', '')
|
||||
else: # EU-Stil: 1.234,56
|
||||
num_extraction_str = num_extraction_str.replace('.', '').replace(',', '.')
|
||||
elif has_comma: # Nur Kommas
|
||||
# Wenn es klar wie ein Dezimalkomma aussieht (z.B. ",dd" am Ende, nur ein Komma)
|
||||
# und nicht wie ein Tausender-Komma (z.B. "8,300" -> soll 8300 werden)
|
||||
if num_extraction_str.count(',') == 1 and re.search(r',\d{1,2}$', num_extraction_str) and not re.search(r',\d{3}(,|\s|\Z)', num_extraction_str):
|
||||
num_extraction_str = num_extraction_str.replace(',', '.')
|
||||
else: # Ansonsten sind Kommas Tausendertrenner (z.B. "8,300" -> "8300")
|
||||
num_extraction_str = num_extraction_str.replace(',', '')
|
||||
elif has_dot: # Nur Punkte
|
||||
# Wenn es klar wie ein Dezimalpunkt aussieht (z.B. ".dd" am Ende, nur ein Punkt)
|
||||
if num_extraction_str.count('.') == 1 and re.search(r'\.\d{1,2}$', num_extraction_str) and not re.search(r'\.\d{3}(?!\d)', num_extraction_str):
|
||||
pass # Punkt ist Dezimal, bleibt
|
||||
else: # Ansonsten sind Punkte Tausendertrenner (z.B. "4.380" -> "4380")
|
||||
num_extraction_str = num_extraction_str.replace('.', '')
|
||||
|
||||
# Schritt 6: Finale Validierung und Konvertierung zu float
|
||||
if not re.fullmatch(r'-?\d+(\.\d+)?', num_extraction_str):
|
||||
logger.debug(f"Kein gültiger numerischer String nach Trennzeichenbehandlung: '{num_extraction_str}' (Original: '{raw_value_str_original}')")
|
||||
return "k.A."
|
||||
|
||||
num_as_float = float(num_extraction_str)
|
||||
|
||||
# Schritt 7: Einheiten-Skalierung
|
||||
scaled_num = num_as_float
|
||||
original_lower = raw_value_str_original.lower() # Verwende den ursprünglichen String für Keyword-Suche
|
||||
|
||||
# Für Wiki-Umsatz: Ziel ist Wert in Millionen
|
||||
if is_umsatz:
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
scaled_num = num_as_float * 1000.0 # z.B. "1,636 Mrd" -> num_as_float=1.636 -> 1636 (Mio)
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
scaled_num = num_as_float / 1000.0 # z.B. "500 Tsd" -> num_as_float=500 -> 0.5 (Mio)
|
||||
# Wenn keine Einheit wie Mrd/Tsd explizit da ist, wird angenommen, dass num_as_float
|
||||
# bereits den Wert in der im Sheet gewünschten Einheit (Mio für Umsatz) darstellt,
|
||||
# *falls die Zahl aus einer Quelle stammt, die bereits in Mio ist (wie CRM)*.
|
||||
# Bei Wikipedia ist das nicht immer der Fall. Wenn "Mio" explizit da steht, ist es gut.
|
||||
# Wenn nur eine Zahl da steht (z.B. "4380" nach Bereinigung von "4.380"),
|
||||
# müssen wir entscheiden, ob das schon Mio sind oder die Grundeinheit.
|
||||
# Da die Spalte S "Wiki Umsatz" als "normalisiert in Mio. €" definiert ist,
|
||||
# sollte diese Funktion einen Wert zurückgeben, der Mio. € darstellt.
|
||||
# Wenn also die Infobox "4,869 billion" (Barilla) hat, wird `num_as_float` zu 4.869.
|
||||
# Der `re.search` für Mrd/billion wird das erkennen und `scaled_num` zu `4869.0` (Mio) machen.
|
||||
# Wenn die Infobox "1.636 Mrd" hat, wird `num_as_float` zu 1.636, `scaled_num` zu 1636 (Mio).
|
||||
# Wenn die Infobox nur "4380" hätte (ohne Mio/Mrd), würde `num_as_float` 4380 sein und `scaled_num` 4380 (Mio).
|
||||
# Das scheint die korrekte Interpretation für die Wiki-Spalte zu sein.
|
||||
|
||||
else: # Mitarbeiter (absolute Zahl)
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
scaled_num = num_as_float * 1000000000.0
|
||||
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower):
|
||||
scaled_num = num_as_float * 1000000.0
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
scaled_num = num_as_float * 1000.0
|
||||
|
||||
# Runde auf Ganzzahl für die Ausgabe im Sheet, aber nur wenn > 0
|
||||
if scaled_num > 0:
|
||||
return str(int(round(scaled_num)))
|
||||
elif scaled_num == 0: # Wenn das Ergebnis exakt 0 ist (z.B. aus "0 Tsd")
|
||||
return "0"
|
||||
else: # Negativ oder Fehler
|
||||
return "k.A."
|
||||
|
||||
except ValueError as e: # Fehler bei float()
|
||||
logger.debug(f"extract_numeric_value: ValueError '{e}' bei Konvertierung von '{num_extraction_str if 'num_extraction_str' in locals() and isinstance(num_extraction_str, str) else raw_value_str_original[:30]}...'")
|
||||
return "k.A."
|
||||
except Exception as e_general:
|
||||
logger.error(f"Unerwarteter Fehler in extract_numeric_value für '{raw_value_str_original[:50]}...': {e_general}")
|
||||
logger.debug(traceback.format_exc())
|
||||
return "k.A."
|
||||
|
||||
# --- Einheiten-Skalierung basierend auf ORIGINALSTRING ---
|
||||
# Pruefe den originalen Wert (kleingeschrieben) auf Einheiten-Keywords
|
||||
original_lower = raw_value_str.lower()
|
||||
multiplier = 1.0
|
||||
|
||||
# Pruefe auf Mrd, Mio, Tsd Keywords und setze den entsprechenden Multiplikator
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
multiplier = 1000000000.0
|
||||
# logger.debug(" -> Einheit: Mrd gefunden") # Zu viel Laerm im Debug
|
||||
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower): # Beruecksichtige "mill."
|
||||
multiplier = 1000000.0
|
||||
# logger.debug(" -> Einheit: Mio gefunden")
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
multiplier = 1000.0
|
||||
# logger.debug(" -> Einheit: Tsd gefunden")
|
||||
|
||||
# Wende den Multiplikator auf die Zahl an
|
||||
num = num * multiplier
|
||||
|
||||
# Konvertiere zu Zielformat und runde ggf.
|
||||
# Rueckgabe als String, wie im Sheet erwartet
|
||||
# Stellen Sie sicher, dass nur positive Werte zurueckgegeben werden (<= 0 ist wie k.A.)
|
||||
if is_umsatz:
|
||||
# Umsatz wird in Millionen € gespeichert (gerundet auf ganze Mio)
|
||||
# Rueckgabe als String
|
||||
umsatz_mio = round(num / 1000000.0)
|
||||
return str(int(umsatz_mio)) if umsatz_mio > 0 else "k.A." # Nur positive Ergebnisse > 0
|
||||
else:
|
||||
# Mitarbeiterzahl wird als ganze Zahl gespeichert (gerundet)
|
||||
# Rueckgabe als String
|
||||
mitarbeiter_int = round(num)
|
||||
return str(int(mitarbeiter_int)) if mitarbeiter_int > 0 else "k.A." # Nur positive Ergebnisse > 0
|
||||
|
||||
|
||||
# --- Numerische Extraktion fuer FILTERLOGIK (gibt 0 statt k.A. zurueck) ---
|
||||
|
||||
Reference in New Issue
Block a user