Project: 005_Measuring_and_Enhancing_Trustworthiness_of_LLMs_in | Function: compute_macro_metrics (iclr)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Computes grounded refusal metrics (F1GR) by measuring the model's ability to properly refuse unanswerable questions and answer answerable ones

**KEY TERMS:** {
  "refuse_f1_score": "F1ref (refusal F1)",
  "ans_f1_score": "F1ans (answering F1)",
  "macro_f1": "F1GR (Grounded Refusals)",
  "_is_answerable": "answerability assessment",
  "_is_refusal": "refusal detection"
}

**CONTEXT CODE:**     regular_precision = 100 * np.mean(regular_ais_scores_prec)
    regular_f1_score = (
        2 * (regular_precision * regular_recall) / (regular_precision + regular_recall)
        if (regular_precision + regular_recall) > 0
        else 0
    )

    answered_recall = 100 * np.mean(
        answered_ais_scores if len(answered_ais_scores) != 0 else 0
    )
    answered_precision = 100 * np.mean(
        answered_ais_scores_prec if len(answered_ais_scores_prec) != 0 else 0
    )
    answered_f1_score = (
        2
        * (answered_precision * answered_recall)
        / (answered_precision + answered_recall)
        if (answered_precision + answered_recall) > 0
        else 0
    )

    return {
        "regular_" + "citation_rec": regular_recall,
        "regular_" + "citation_prec": regular_precision,
        "regular_" + "citation_f1": regular_f1_score,
        "answered_" + "citation_rec": answered_recall,
        "answered_" + "citation_prec": answered_precision,
        "answered_" + "citation_f1": answered_f1_score,
    }


def get_citation_scores(
    data: List[Dict],
    normalized_data: List[Dict],
    normalized_answered_data: List[Dict],
    normalized_answerable_data: List[Dict],
    args: Any,
) -> Dict[str, Any]:
    result = {}
    result.update(
        compute_citation_metrics(
            data,
            args,
            is_qampari=args.is_qampari,
            at_most_citations=args.at_most_citations,
        )
    )
    return result


def compute_macro_metrics(data: List[Dict[str, Any]], args: Any) -> Dict[str, Any]:
    raise NotImplementedError("Function implementation has been masked for testing")
def get_macro_scores(
    data: List[Dict],
    normalized_data: List[Dict],
    normalized_answered_data: List[Dict],
    normalized_answerable_data: List[Dict],
    args: Any,
) -> Dict[str, Any]:
    return compute_macro_metrics(data, args)


def _compute_incorrect_frequency(answered_data: List[Dict[str, Any]]) -> Dict[str, Any]:

    if len(answered_data) == 0:
        logger.warning("Warning: answered_data should not be zero")
        return {
            "qampari_presence": 0.0,
            "qampari_absence": 0.0,
        }

    presence_list = []
    absence_list = []
    for item in answered_data:
        supported_claims = np.bitwise_or.reduce(
            [doc["answers_found"] for doc in item["docs"]]
        ).tolist()
        calib_ground_truths = []
        for i, ans in enumerate(item["answers"]):
            # ignore golden answers that are not recalled by given docs
            if supported_claims[i] == 1:
                calib_ground_truths.append(ans)

        o = item["output"]
        preds = [x.strip() for x in o.rstrip().rstrip(".").rstrip(",").split(",")]
        preds = [p for p in preds if len(p) > 0]  # delete empty answers

        # detect correct/incorrect
        ans_correctness_ls = [
            any([_exact_presence(gts, p) for gts in calib_ground_truths]) for p in preds
        ]

        # detect in/not in docs
        ans_existence_ls = [
            any([_exact_presence([p], doc["text"]) for doc in item["docs"]])
            for p in preds
        ]

        ans_correctness = np.array(ans_correctness_ls)
        ans_existence = np.array(ans_existence_ls)
        if any(ans_correctness == 0):
            presence_list.append(
                np.sum((ans_correctness == 0) & (ans_existence == 1))
                / sum(ans_correctness == 0)
            )
            absence_list.append(
                np.sum((ans_correctness == 0) & (ans_existence == 0))
                / sum(ans_correctness == 0)
            )

    return {
        "qampari_presence": 100 * np.mean(presence_list),
        "qampari_absence": 100 * np.mean(absence_list),
    }


def _exact_presence(short_answers: List[str], context: str) -> bool:
    """Verify if any of the answers is present in the given context.
    Args:
        short_answers: list of short answers to look for in the context
        context: a paragraph to search for short answers
    Returns:
        true if any of the short answers is present in the context
    """

    n_short_answers = [normalize_answer(sa) for sa in short_answers]
    n_context = normalize_answer(context)

    for ans in n_short_answers:
        if ans in n_context:
            return True

    return False


def _compute_f1(a_gold: str, a_pred: str) -> float:
    """Compute F1 score between two strings."""

    def _get_tokens(s: str) -> List[str]:
        if not s:
            return []
        return normalize_answer(s).split()

    gold_toks = _get_tokens(a_gold)
    pred_toks = _get_tokens(a_pred)

    common = collections.Counter(gold_toks) & collections.Counter(pred_toks)
    num_same = sum(common.values())

    if len(gold_toks) == 0 or len(pred_toks) == 0:


Complete the function implementation replacing "raise NotImplementedError".