Spaces:
Running
Running
annotation review app (per-user queues, Hub-backed rulings, static-safe direct commit)
53ea208 verified Download merge.py from rafmacalaba/data-use-annotate: direct link, hf CLI and curl.
- Browser
- Download file 2.33 kB
-
https://huggingface.co/spaces/rafmacalaba/data-use-annotate/resolve/main/merge.py
- Command line
-
hf download hf://spaces/rafmacalaba/data-use-annotate/merge.py
-
curl -L -o merge.py https://huggingface.co/spaces/rafmacalaba/data-use-annotate/resolve/main/merge.py
2.33 kB
| #!/usr/bin/env python3 | |
| """Reconcile annotation files into one adjudicated row per key. | |
| uv run python human_labeling/merge.py [--out annotations/adjudicated.jsonl] | |
| Reads every annotations/rulings*.jsonl + annotations/*-export.json | |
| (export format: {"items": [...]}, same ruling objects), majority-votes | |
| ties -> "REVIEW", and writes adjudicated.jsonl: | |
| {"key", "verdict", "n_keep", "n_drop", "annotators", "origins"?, ...} | |
| Kept inputs are never modified; rerun any time. | |
| """ | |
| import argparse | |
| import glob | |
| import json | |
| from pathlib import Path | |
| HERE = Path(__file__).resolve().parent | |
| ANNOT = HERE / "annotations" | |
| def load() -> list[dict]: | |
| rulings: list[dict] = [] | |
| for f in sorted(glob.glob(str(ANNOT / "rulings*.jsonl"))): | |
| for line in open(f): | |
| line = line.strip() | |
| if line: | |
| rulings.append(json.loads(line)) | |
| for f in sorted(glob.glob(str(ANNOT / "*-export.json"))): | |
| try: | |
| payload = json.load(open(f)) | |
| except json.JSONDecodeError: | |
| continue | |
| items = payload.get("items", payload if isinstance(payload, list) else []) | |
| rulings.extend(i for i in items if isinstance(i, dict) and i.get("key")) | |
| return rulings | |
| def main() -> None: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--out", default=str(ANNOT / "adjudicated.jsonl")) | |
| a = ap.parse_args() | |
| by_key: dict[str, list[dict]] = {} | |
| for r in load(): | |
| by_key.setdefault(r["key"], []).append(r) | |
| n_tie = 0 | |
| with open(a.out, "w") as f: | |
| for key in sorted(by_key): | |
| rs = by_key[key] | |
| keep = sum(1 for r in rs if r["ruling"] == "DATA_MENTION") | |
| drop = sum(1 for r in rs if r["ruling"] == "NON_MENTION") | |
| verdict = "DATA_MENTION" if keep > drop else "NON_MENTION" if drop > keep else "REVIEW" | |
| n_tie += verdict == "REVIEW" | |
| f.write(json.dumps({ | |
| "key": key, "verdict": verdict, "n_keep": keep, "n_drop": drop, | |
| "n": len(rs), "annotators": sorted({r.get("annotator", "?") for r in rs}), | |
| "surface": rs[0].get("surface"), "origin": rs[0].get("origin"), | |
| "head_score": rs[0].get("head_score"), | |
| }) + "\n") | |
| print(f"keys={len(by_key)} ties={n_tie} -> {a.out}") | |
| if __name__ == "__main__": | |
| main() | |