def MakeChangePoint(series, split_index): """Makes a ChangePoint object for the given series at the given point. Args: series: A list of (x, y) pairs. split_index: Index of the first point after the split. Returns: A ChangePoint object. """ assert 0 <= split_index < len(series) x_values, y_values = zip(*series) left, right = y_values[:split_index], y_values[split_index:] left_median, right_median = math_utils.Median(left), math_utils.Median( right) ttest_results = ttest.WelchsTTest(left, right) return ChangePoint( x_value=x_values[split_index], median_before=left_median, median_after=right_median, size_before=len(left), size_after=len(right), window_start=x_values[0], window_end=x_values[-1], # inclusive bound relative_change=math_utils.RelativeChange(left_median, right_median), std_dev_before=math_utils.StandardDeviation(left), t_statistic=ttest_results.t, degrees_of_freedom=ttest_results.df, p_value=ttest_results.p)
def _PassesThresholds(values, split_index, min_segment_size, min_absolute_change, min_relative_change, min_steppiness, multiple_of_std_dev): """Checks whether a point in a series appears to be an change point. Args: values: A list of numbers. split_index: An index in the list of numbers. min_segment_size: Threshold for size of segments before or after a point. min_absolute_change: Minimum absolute median change threshold. min_relative_change: Minimum relative median change threshold. min_steppiness: Threshold for how similar to a step a change point must be. multiple_of_std_dev: Threshold for change as multiple of std. deviation. Returns: True if it passes all of the thresholds, False otherwise. """ left, right = values[:split_index], values[split_index:] left_median, right_median = math_utils.Median(left), math_utils.Median( right) # 1. Segment size filter. if len(left) < min_segment_size or len(right) < min_segment_size: return False # 2. Absolute change filter. absolute_change = abs(left_median - right_median) if absolute_change < min_absolute_change: return False # 3. Relative change filter. relative_change = math_utils.RelativeChange(left_median, right_median) if relative_change < min_relative_change: return False # 4. Multiple of standard deviation filter. min_std_dev = min(math_utils.StandardDeviation(left), math_utils.StandardDeviation(right)) if absolute_change < multiple_of_std_dev * min_std_dev: return False # 5. Steppiness filter. steppiness = find_step.Steppiness(values, split_index) if steppiness < min_steppiness: return False # Passed all filters! return True
def _IsApproximatelyEqual(delta1, delta2): smaller = min(delta1, delta2) larger = max(delta1, delta2) return math_utils.RelativeChange(smaller, larger) <= _MAX_DELTA_DIFFERENCE
def testRelativeChange_FromZero_ReturnsInf(self): self.assertEqual(float('inf'), math_utils.RelativeChange(0, 1))
def testRelativeChange_NoChange_ReturnsZero(self): self.assertEqual(0, math_utils.RelativeChange(7, 7))
def testRelativeChange(self): # The relative difference is with respect to the first number, and the # absolute value is taken. So 1 means doubling, and 0.5 means halving. self.assertEqual(1, math_utils.RelativeChange(32, 64)) self.assertEqual(0.5, math_utils.RelativeChange(64, 32))