forked from Yiyang-Ian-Li/LongDA
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
154 lines (124 loc) · 4.31 KB
/
Copy pathutils.py
File metadata and controls
154 lines (124 loc) · 4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import ast
import json
import re
from pathlib import Path
from typing import Any, Dict, Iterable, List
_NUMBER_PATTERN = re.compile(r"[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?")
def _standardize_numeric_text(text: str) -> str:
"""
Normalize numeric annotations (%, ±, thousand separators) for safer parsing.
"""
replacements = {
"−": "-",
"–": "-",
"—": "-",
"±": "+-",
"+/-": "+-",
}
for src, dst in replacements.items():
text = text.replace(src, dst)
# Drop ± tolerances, keep the central value.
text = re.sub(
r"([-+]?\d[\d,]*\.?\d*(?:[eE][-+]?\d+)?)\s*\+-\s*[-+]?\d[\d,]*\.?\d*(?:[eE][-+]?\d+)?",
r"\1",
text,
)
text = text.replace("%", "")
# Remove thousand separators that sit between digits (e.g., 1,234).
text = re.sub(r"(?<=\d),(?=\d{3}\b)", "", text)
return text
def _try_literal_eval(text: str) -> Any:
try:
return ast.literal_eval(text)
except (ValueError, SyntaxError):
return None
def _extract_numbers(text: str) -> Iterable[float]:
return [float(match) for match in _NUMBER_PATTERN.findall(text)]
def _extract_key_value_pairs(text: str) -> Dict[str, Any]:
"""
Attempt to extract simple "key: value" pairs from free-form text.
"""
pattern = re.compile(
r"([A-Za-z0-9_\-/\s]+?)\s*[:=]\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)"
)
pairs = pattern.findall(text)
if not pairs:
return {}
return {key.strip(): float(value) for key, value in pairs}
def _collect_files(path: Path) -> List[str]:
if not path.exists():
return []
collected: List[str] = []
entries = sorted(path.iterdir(), key=lambda item: item.name.lower())
for entry in entries:
if entry.is_file():
collected.append(str(entry))
elif entry.is_dir():
collected.extend(_collect_files(entry))
return collected
def get_data_and_doc_paths(survey):
data_files = []
doc_files = []
base = Path(f"benchmark/{survey}")
data_dir = base / "data"
docs_dir = base / "docs"
data_files.extend(_collect_files(data_dir))
doc_files.extend(_collect_files(docs_dir))
# Deduplicate while preserving order.
def _unique(sequence):
seen = set()
for item in sequence:
if item not in seen:
seen.add(item)
yield item
return list(_unique(data_files)), list(_unique(doc_files))
def normalize_answer_value(value: Any) -> Any:
"""
Convert agent or benchmark answers into comparable numeric structures.
Returns floats, lists, dicts, or None when parsing fails.
"""
if value is None:
return None
if isinstance(value, (int, float)):
return float(value)
if isinstance(value, dict):
return {
str(key): normalize_answer_value(val)
for key, val in value.items()
}
if isinstance(value, (list, tuple, set)):
normalized = [normalize_answer_value(item) for item in value]
return normalized
if isinstance(value, str):
stripped = value.strip()
if not stripped:
return None
cleaned = _standardize_numeric_text(stripped)
literal = _try_literal_eval(cleaned)
if literal is not None:
return normalize_answer_value(literal)
kv_pairs = _extract_key_value_pairs(cleaned)
if kv_pairs:
return {key: normalize_answer_value(val) for key, val in kv_pairs.items()}
numbers = _extract_numbers(cleaned)
if numbers:
if len(numbers) == 1:
return float(numbers[0])
return [float(num) for num in numbers]
return None
# Fallback: attempt to serialize complex objects, otherwise return None.
try:
return normalize_answer_value(json.loads(value))
except Exception:
return None
def build_answer_entry(raw_answer: Any, code: str = "") -> Dict[str, Any]:
"""
Prepare the payload written to disk for each question.
"""
raw_text = raw_answer if isinstance(raw_answer, str) else json.dumps(raw_answer, ensure_ascii=False)
entry = {
"raw_answer": raw_text,
"parsed_answer": normalize_answer_value(raw_answer),
"code": code.strip() if isinstance(code, str) else "",
}
return entry