"""Data cleaning helpers: numeric coercion for string columns.""" import polars as pl def _coerce_to_float(series: pl.Series) -> pl.Series: """Convert string series to Float64. Every entry that doesn't parse as a valid float becomes null. Handles blank strings, standalone '+'/'-', 'N/A', 'null', 'None', etc. """ series = series.cast(pl.Utf8).str.strip_chars() artifacts = ('', '+', '-', '.', '..', 'N/A', 'n/a', 'NA', 'null', 'None', 'NULL', '?', '---', '--', '/') is_artifact = series.is_in(pl.Series(artifacts).implode()) result = [] for v, bad in zip(series.to_list(), is_artifact.to_list()): if v is None or bad: result.append(None) else: # Check for "+"/"-" prefix artifacts (e.g., "+abc", "-xyz") # where the value isn't a genuine numeric string. Values like # "+5" or "-3.14" are still accepted as valid floats below. if len(v) > 1 and v[0] in '+-': try: float(v[1:]) # validate suffix minus prefix except ValueError: result.append(None) continue try: result.append(float(v)) except (ValueError, TypeError): result.append(None) return pl.Series(result, dtype=pl.Float64)