def _eval_metric(
    anomalies: Union[TimeSeries, Sequence[TimeSeries]],
    pred_series: Union[TimeSeries, Sequence[TimeSeries]],
    window: Union[int, Sequence[int]],
    metric: Literal["AUC_ROC", "AUC_PR", "recall", "precision", "f1", "accuracy"],
    pred_is_binary: bool,
):
    """Computes a score/metric between anomaly scores or binary predicted anomalies against true
    anomalies.

    Parameters
    ----------
    anomalies
        The (sequence of) ground truth binary anomaly series (`1` if it is an anomaly and `0` if not).
    pred_series
        The (sequence of) anomaly scores or predicted binary anomaly series.
    window
        Integer value indicating the number of past samples each point represents in the `pred_scores`.
        The parameter will be used to transform `anomalies`.
        If a list of integers, the length must match the number of series in `pred_scores`.
        If an integer, the value will be used for every series in `pred_scores` and `anomalies`.
    metric
        The name of the scoring function to use. Must be one of "recall", "precision",
        "f1", and "accuracy" if `pred_is_binary` is `True`. Otherwise, must be one of "AUC_ROC", "AUC_PR".
    pred_is_binary
        Whether `pred_series` refers predicted binary anomalies or anomaly scores.

    Returns
    -------
    float
        A single score for univariate `pred_series` series (with only one component/column).
    Sequence[float]
        A sequence (list) of scores for:

        - multivariate `pred_series` series (multiple components). Gives a score for each component.
        - a sequence (list) of univariate `pred_series` series. Gives a score for each series.
    Sequence[Sequence[float]]
        A sequence of sequences of scores for a sequence of multivariate `pred_series` series.
        Gives a score for each series (outer sequence) and component (inner sequence).
    """
    metrics_exp = (
        {"recall", "precision", "f1", "accuracy"}
        if pred_is_binary
        else {"AUC_ROC", "AUC_PR"}
    )
    if metric not in metrics_exp:
        raise_log(
            ValueError(f"Argument `metric` must be one of {metrics_exp}"),
            logger=logger,
        )

    if metric == "AUC_ROC":
        metric_fn = roc_auc_score
    elif metric == "AUC_PR":
        metric_fn = average_precision_score
    elif metric == "recall":
        metric_fn = recall_score
    elif metric == "precision":
        metric_fn = precision_score
    elif metric == "f1":
        metric_fn = f1_score
    else:
        metric_fn = accuracy_score

    called_with_single_series = isinstance(pred_series, TimeSeries)
    anomalies = series2seq(anomalies)
    pred_series = series2seq(pred_series)
    window = [window] if not isinstance(window, Sequence) else window

    if len(anomalies) == 1 and len(pred_series) > 1:
        anomalies = anomalies * len(pred_series)

    name = "anomalies"
    pred_name = "pred_anomalies" if pred_is_binary else "pred_scores"
    _assert_same_length(
        anomalies,
        pred_series,
        name,
        pred_name,
    )

    if len(window) == 1:
        window = window * len(anomalies)
    else:
        if len(window) != len(anomalies):
            raise_log(
                ValueError(
                    f"The list of windows must be the same length as the list of `{pred_name}` and "
                    f"`{name}`. There must be one window value for each series. "
                    f"Found length {len(window)}, expected {len(anomalies)}."
                ),
                logger=logger,
            )

    sol = []
    for s_anomalies, s_pred, s_window in zip(anomalies, pred_series, window):
        _assert_timeseries(s_pred, name=pred_name)
        _assert_timeseries(s_anomalies, name=name)
        _assert_binary(s_anomalies, name)
        if pred_is_binary:
            _assert_binary(s_pred, pred_name)

        # if s_window > 1, the anomalies will be adjusted so that it can be compared timewise with s_pred
        s_anomalies = _max_pooling(s_anomalies, s_window)

        _sanity_check_two_series(s_pred, s_anomalies, pred_name, name)

        s_pred_vals = s_pred.slice_intersect_values(s_anomalies, copy=False)
        s_anomalies_vals = s_anomalies.slice_intersect_values(s_pred, copy=False)

        if not len(s_pred_vals) == len(s_anomalies_vals):
            raise_log(
                ValueError(
                    f"The two time series `{pred_name}` and `{name}` "
                    f"must have at least a partially overlapping time index."
                ),
                logger=logger,
            )

        if not pred_is_binary:  # `pred_series` is an anomaly score
            nr_anomalies_per_component = s_anomalies_vals.sum(axis=0).flatten()

            if nr_anomalies_per_component.min() == 0:
                raise_log(
                    ValueError(
                        f"`{name}` does not contain anomalies. {metric} cannot be computed."
                    ),
                    logger=logger,
                )
            if nr_anomalies_per_component.max() == len(s_anomalies_vals):
                add_txt = (
                    ""
                    if s_window <= 1
                    else f" Consider decreasing the window size (window={s_window})"
                )
                raise_log(
                    ValueError(
                        f"`{name}` only contains anomalies. {metric} cannot be computed."
                        + add_txt
                    ),
                    logger=logger,
                )

        # TODO: could we vectorize this?
        metrics = []
        for component_idx in range(s_pred.width):
            metrics.append(
                metric_fn(
                    s_anomalies_vals[:, component_idx],
                    s_pred_vals[:, component_idx],
                )
            )
        sol.append(metrics if len(metrics) > 1 else metrics[0])

    return sol[0] if called_with_single_series else sol
Computes a score/metric between anomaly scores or binary predicted anomalies against true
    anomalies.

    Parameters
    ----------
    anomalies
        The (sequence of) ground truth binary anomaly series (`1` if it is an anomaly and `0` if not).
    pred_series
        The (sequence of) anomaly scores or predicted binary anomaly series.
    window
        Integer value indicating the number of past samples each point represents in the `pred_scores`.
        The parameter will be used to transform `anomalies`.
        If a list of integers, the length must match the number of series in `pred_scores`.
        If an integer, the value will be used for every series in `pred_scores` and `anomalies`.
    metric
        The name of the scoring function to use. Must be one of "recall", "precision",
        "f1", and "accuracy" if `pred_is_binary` is `True`. Otherwise, must be one of "AUC_ROC", "AUC_PR".
    pred_is_binary
        Whether `pred_series` refers predicted binary anomalies or anomaly scores.

    Returns
    -------
    float
        A single score for univariate `pred_series` series (with only one component/column).
    Sequence[float]
        A sequence (list) of scores for:

        - multivariate `pred_series` series (multiple components). Gives a score for each component.
        - a sequence (list) of univariate `pred_series` series. Gives a score for each series.
    Sequence[Sequence[float]]
        A sequence of sequences of scores for a sequence of multivariate `pred_series` series.
        Gives a score for each series (outer sequence) and component (inner sequence).
    
darts/ad/utils.py