haiku.rag/evaluations/evaluations/numbers.py
2026-06-06 14:52:04 +03:00

66 lines
2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
_SCALE = {
"thousand": 1e3,
"million": 1e6,
"billion": 1e9,
"trillion": 1e12,
}
_NUMBER_RE = re.compile(r"[(-]?\$?\d[\d,]*(?:\.\d+)?%?\)?")
_SCALED_RE = re.compile(
r"([(-]?\$?\d[\d,]*(?:\.\d+)?)\s*(thousand|million|billion|trillion)",
re.IGNORECASE,
)
def _to_float(token: str) -> float | None:
stripped = token.lstrip()
negative = stripped.startswith("(") or stripped.startswith("-")
cleaned = (
token.replace("$", "")
.replace(",", "")
.replace("%", "")
.replace("(", "")
.replace(")", "")
.strip()
.lstrip("-")
)
if not cleaned or cleaned == ".":
return None
try:
value = float(cleaned)
except ValueError:
return None
return -value if negative else value
def extract_numbers(text: str) -> list[float]:
"""Pull numeric values out of free-form text.
Handles currency, thousands separators, trailing percent signs, and
parenthesised negatives. Numbers qualified by a scale word ("1.2 million")
contribute both the raw and the scaled value, so either phrasing can match.
"""
text = text.replace("", "-") # normalize the typographic minus sign
numbers: list[float] = []
for token in _NUMBER_RE.findall(text):
value = _to_float(token)
if value is not None:
numbers.append(value)
if "%" in token:
# Gold answers store ratios as either a percent (24.69) or a
# decimal (0.935); offer both readings of a percent figure.
numbers.append(value / 100)
for number, scale in _SCALED_RE.findall(text):
value = _to_float(number)
if value is not None:
numbers.append(value * _SCALE[scale.lower()])
return numbers
def numbers_close(value: float, target: float, eps: float = 0.01) -> bool:
"""Whether ``value`` matches ``target`` within relative tolerance ``eps``."""
if target == 0:
return value == 0
return abs(value - target) / abs(target) <= eps