"""对人工构造的分类结果计算混淆矩阵与指标；不是模型跑分。"""
from collections import Counter

LABELS = ("billing", "security", "general", "unknown")
# None 是缺失响应；它仍然留在总样本数中。
gold = {"a": "security", "b": "security", "c": "general", "d": "billing", "e": "unknown"}
pred = {"a": "security", "b": "general", "c": "security", "d": "billing", "e": None}
matrix = Counter((truth, pred.get(key)) for key, truth in gold.items())

# 分母为零时返回 None，表示未定义；不要伪装成完美结果。
def ratio(numerator, denominator):
    return numerator / denominator if denominator else None

f1_scores = []
for label in LABELS:
    tp = matrix[label, label]
    fp = sum(n for (truth, guess), n in matrix.items() if guess == label and truth != label)
    fn = sum(n for (truth, guess), n in matrix.items() if truth == label and guess != label)
    precision = ratio(tp, tp + fp)
    recall = ratio(tp, tp + fn)
    # 直接用计数形式计算 F1，避免在 precision 未定义时发生除法错误。
    f1 = ratio(2 * tp, 2 * tp + fp + fn)
    f1_scores.append(f1)
    print(label, {"TP": tp, "FP": fp, "FN": fn, "precision": precision, "recall": recall, "f1": f1})

correct = sum(truth == pred.get(key) for key, truth in gold.items())
print("准确率：", correct / len(gold))
# 本数据中每个类别都有真实样本，F1 都有定义；其他数据需报告缺失类别。
if all(value is not None for value in f1_scores):
    print("Macro-F1：", sum(f1_scores) / len(f1_scores))
print("混淆计数：", dict(matrix))
