Source code for gluonts.nursery.anomaly_detection.supervised_metrics._segment_precision_recall

# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.

from typing import List, Tuple

import numpy as np

from .utils import labels_to_ranges, range_overlap


[docs]def segment_precision_recall( real_ranges: List[range], pred_ranges: List[range] ) -> Tuple[float, float]: """ Segment based metric is less lenient than the range based metric. This metric counts * a ground truth anomaly range as true-positive as long as there is an overlapping predicted anomaly range; it does not penalize the position of the overlap. If there is no overlapping predicted range for given ground truth range, then it counts as one false-negative irrespective of its size. * a predicted anomaly range as false positive if it has a nonempty overlap with a "normal" range. Parameters ---------- real_ranges List of ranges corresponding to ground truth anomalies. pred_ranges List of predicted anomaly ranges. Returns ------- Tuple containing segment based precision and recall. """ # Compute the number of true positives and false negatives by going over each of the ground truth anomaly ranges tp, fn = 0, 0 for tr in real_ranges: tp_found = False for pr in pred_ranges: if range_overlap(tr, pr): tp_found = True break if tp_found: tp += 1 else: fn += 1 # Deduce the ranges corresponding to normal behavior if real_ranges and real_ranges[-1]: max_ix_real_range = real_ranges[-1][-1] else: max_ix_real_range = -1 if pred_ranges and pred_ranges[-1]: max_ix_pred_range = pred_ranges[-1][-1] else: max_ix_pred_range = -1 num_time_points = max(max_ix_real_range, max_ix_pred_range) + 1 normal_ind = np.array([True] * num_time_points) for tr in real_ranges: normal_ind[tr] = False normal_ranges = labels_to_ranges(normal_ind) # Compute the number of false positives and true negatives by going over each of the ground truth normal ranges fp, tn = 0, 0 for nr in normal_ranges: fp_found = False for pr in pred_ranges: if range_overlap(nr, pr): fp_found = True fp += 1 if not fp_found: tn += 1 # avoid division by zero. return tp / max(tp + fp, 1), tp / max(tp + fn, 1)