• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""The database model for an "Anomaly", which represents a step up or down."""
6
7import sys
8
9from google.appengine.ext import ndb
10
11from dashboard.models import alert
12
13# A string to describe the magnitude of a change from zero to non-zero.
14FREAKIN_HUGE = 'zero-to-nonzero'
15
16# Possible improvement directions for a change. An Anomaly will always have a
17# direction of UP or DOWN, but a test's improvement direction can be UNKNOWN.
18UP, DOWN, UNKNOWN = (0, 1, 4)
19
20
21class Anomaly(alert.Alert):
22  """Represents a change-point or step found in the data series for a Test.
23
24  An Anomaly can be an upward or downward change, and can represent an
25  improvement or a regression.
26  """
27  # The number of points before and after this anomaly that were looked at
28  # when finding this anomaly.
29  segment_size_before = ndb.IntegerProperty(indexed=False)
30  segment_size_after = ndb.IntegerProperty(indexed=False)
31
32  # The medians of the segments before and after the anomaly.
33  median_before_anomaly = ndb.FloatProperty(indexed=False)
34  median_after_anomaly = ndb.FloatProperty(indexed=False)
35
36  # The standard deviation of the segments before the anomaly.
37  std_dev_before_anomaly = ndb.FloatProperty(indexed=False)
38
39  # The number of points that were used in the before/after segments.
40  # This is also  returned by FindAnomalies
41  window_end_revision = ndb.IntegerProperty(indexed=False)
42
43  # In order to estimate how likely it is that this anomaly is due to noise,
44  # t-test may be performed on the points before and after. The t-statistic,
45  # degrees of freedom, and p-value are potentially-useful intermediary results.
46  t_statistic = ndb.FloatProperty(indexed=False)
47  degrees_of_freedom = ndb.FloatProperty(indexed=False)
48  p_value = ndb.FloatProperty(indexed=False)
49
50  # Whether this anomaly represents an improvement; if false, this anomaly is
51  # considered to be a regression.
52  is_improvement = ndb.BooleanProperty(indexed=True, default=False)
53
54  # Whether this anomaly recovered (i.e. if this is a step down, whether there
55  # is a corresponding step up later on, or vice versa.)
56  recovered = ndb.BooleanProperty(indexed=True, default=False)
57
58  @property
59  def percent_changed(self):
60    """The percent change from before the anomaly to after."""
61    if self.median_before_anomaly == 0.0:
62      return sys.float_info.max
63    difference = self.median_after_anomaly - self.median_before_anomaly
64    return 100 * difference / self.median_before_anomaly
65
66  @property
67  def direction(self):
68    """Whether the change is numerically an increase or decrease."""
69    if self.median_before_anomaly < self.median_after_anomaly:
70      return UP
71    return DOWN
72
73  def GetDisplayPercentChanged(self):
74    """Gets a string showing the percent change."""
75    if abs(self.percent_changed) == sys.float_info.max:
76      return FREAKIN_HUGE
77    else:
78      return str('%.1f%%' % abs(self.percent_changed))
79
80  def SetIsImprovement(self, test=None):
81    """Sets whether the alert is an improvement for the given test."""
82    if not test:
83      test = self.test.get()
84    # |self.direction| is never equal to |UNKNOWN| (see the definition above)
85    # so when the test improvement direction is |UNKNOWN|, |self.is_improvement|
86    # will be False.
87    self.is_improvement = (self.direction == test.improvement_direction)
88