summary_llm_score.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import argparse
  4. import json
  5. from collections import defaultdict
  6. TARGET_TAGS = {"Solo commentators", "Multiple commentators", "Guidance"}
  7. def safe_float(x):
  8. try:
  9. return float(x)
  10. except Exception:
  11. return None
  12. def load_jsonl(path: str):
  13. rows = []
  14. with open(path, "r", encoding="utf-8") as f:
  15. for ln, line in enumerate(f, 1):
  16. line = line.strip()
  17. if not line:
  18. continue
  19. try:
  20. rows.append(json.loads(line))
  21. except Exception as e:
  22. print(f"[WARN] line {ln} parse failed: {e}")
  23. return rows
  24. def init_stat(fields):
  25. d = {"cnt": 0}
  26. for k in fields:
  27. d[f"sum_{k}"] = 0.0
  28. return d
  29. def aggregate(rows, score_key: str, fields: list[str]):
  30. stats_by_tag = defaultdict(lambda: init_stat(fields))
  31. stats_merged = init_stat(fields)
  32. for r in rows:
  33. tag = r.get("tag", "UNKNOWN")
  34. score = r.get(score_key) or {}
  35. vals = []
  36. ok = True
  37. for k in fields:
  38. v = safe_float(score.get(k))
  39. if v is None:
  40. ok = False
  41. break
  42. vals.append(v)
  43. # Count only rows where all fields exist (avoid skew from dirty data)
  44. if not ok:
  45. continue
  46. # per tag
  47. s = stats_by_tag[tag]
  48. s["cnt"] += 1
  49. for k, v in zip(fields, vals):
  50. s[f"sum_{k}"] += v
  51. # merged for target tags
  52. if tag in TARGET_TAGS:
  53. stats_merged["cnt"] += 1
  54. for k, v in zip(fields, vals):
  55. stats_merged[f"sum_{k}"] += v
  56. return stats_by_tag, stats_merged
  57. def mean_row(stat, fields):
  58. cnt = stat["cnt"]
  59. if cnt <= 0:
  60. return None
  61. means = [stat[f"sum_{k}"] / cnt for k in fields]
  62. overall = sum(means) / len(means)
  63. return cnt, means, overall
  64. def print_table(stats_by_tag, stats_merged, fields, score_key):
  65. print(f"\n=== Mean stats for {score_key} by tag ===")
  66. col_w = 24
  67. header = f"{'Tag':<{col_w}} {'N':>6} " + " ".join([f"{k:>12}" for k in fields]) + f" {'Overall':>12}"
  68. print(header)
  69. print("-" * len(header))
  70. # sort by count desc
  71. for tag, stat in sorted(stats_by_tag.items(), key=lambda x: x[1]["cnt"], reverse=True):
  72. row = mean_row(stat, fields)
  73. if row is None:
  74. continue
  75. cnt, means, overall = row
  76. line = f"{tag:<{col_w}} {cnt:>6} " + " ".join([f"{m:>12.4f}" for m in means]) + f" {overall:>12.4f}"
  77. print(line)
  78. print("\n=== Merged mean for tags: Solo commentators / Multiple commentators / Guidance ===")
  79. row = mean_row(stats_merged, fields)
  80. if row is None:
  81. print("No valid samples found for the three target tags.")
  82. return
  83. cnt, means, overall = row
  84. kv = ", ".join([f"{k}={m:.4f}" for k, m in zip(fields, means)])
  85. print(f"N={cnt}, {kv}, Overall={overall:.4f}")
  86. def main():
  87. parser = argparse.ArgumentParser()
  88. parser.add_argument("-i", "--input", required=True, help="Path to jsonl file (one JSON per line)")
  89. parser.add_argument("--score_key", required=True, help="Score field name, e.g. liveu_score / finalq_score")
  90. parser.add_argument("--fields", nargs="+", required=True, help="Fields under score_key, e.g. Time Rate TextU")
  91. args = parser.parse_args()
  92. rows = load_jsonl(args.input)
  93. stats_by_tag, stats_merged = aggregate(rows, args.score_key, args.fields)
  94. print_table(stats_by_tag, stats_merged, args.fields, args.score_key)
  95. if __name__ == "__main__":
  96. main()
  97. # python summary_llm_score.py --score_key liveu_score --fields Time Rate TextU -i data.jsonl