blob: 8b34f631d91de579d50021367d68779620cfde3e [file] [log] [blame]
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import numbers
import math
from telemetry import value as value_module
from telemetry.value import none_values
from telemetry.value import summarizable
def Variance(sample):
""" Compute the population variance.
Args:
sample: a list of numbers.
"""
k = len(sample) - 1 # Bessel correction
if k <= 0:
return 0
m = _Mean(sample)
return sum((x - m)**2 for x in sample)/k
def StandardDeviation(sample):
""" Compute standard deviation for a list of numbers.
Args:
sample: a list of numbers.
"""
return math.sqrt(Variance(sample))
def PooledStandardDeviation(list_of_samples, list_of_variances=None):
""" Compute standard deviation for a list of samples.
See: https://en.wikipedia.org/wiki/Pooled_variance for the formula.
Args:
list_of_samples: a list of lists, each is a list of numbers.
list_of_variances: a list of numbers, the i-th element is the variance of
the i-th sample in list_of_samples. If this is None, we use
Variance(sample) to get the variance of the i-th sample.
"""
pooled_variance = 0.0
total_degrees_of_freedom = 0
for i in xrange(len(list_of_samples)):
l = list_of_samples[i]
k = len(l) - 1 # Bessel correction
if k <= 0:
continue
variance = list_of_variances[i] if list_of_variances else Variance(l)
pooled_variance += k * variance
total_degrees_of_freedom += k
if total_degrees_of_freedom:
return (pooled_variance/total_degrees_of_freedom) ** 0.5
return 0
def _Mean(values):
return float(sum(values)) / len(values) if len(values) > 0 else 0.0
class ListOfScalarValues(summarizable.SummarizableValue):
""" ListOfScalarValues represents a list of numbers.
By default, std is the standard deviation of all numbers in the list. Std can
also be specified in the constructor if the numbers are not from the same
population.
"""
def __init__(self, page, name, units, values,
important=True, description=None,
tir_label=None, none_value_reason=None,
std=None, same_page_merge_policy=value_module.CONCATENATE,
improvement_direction=None, grouping_keys=None):
super(ListOfScalarValues, self).__init__(page, name, units, important,
description, tir_label,
improvement_direction,
grouping_keys)
if values is not None:
assert isinstance(values, list)
assert len(values) > 0
assert all(isinstance(v, numbers.Number) for v in values)
assert std is None or isinstance(std, numbers.Number)
else:
assert std is None
none_values.ValidateNoneValueReason(values, none_value_reason)
self.values = values
self.none_value_reason = none_value_reason
self.same_page_merge_policy = same_page_merge_policy
if values is not None and std is None:
std = StandardDeviation(values)
assert std is None or std >= 0, (
'standard deviation cannot be negative: %s' % std)
self._std = std
@property
def std(self):
return self._std
@property
def variance(self):
return self._std ** 2
def __repr__(self):
if self.page:
page_name = self.page.display_name
else:
page_name = 'None'
if self.same_page_merge_policy == value_module.CONCATENATE:
merge_policy = 'CONCATENATE'
else:
merge_policy = 'PICK_FIRST'
return ('ListOfScalarValues(%s, %s, %s, %s, '
'important=%s, description=%s, tir_label=%s, std=%s, '
'same_page_merge_policy=%s, improvement_direction=%s, '
'grouping_keys=%s)') % (
page_name,
self.name,
self.units,
repr(self.values),
self.important,
self.description,
self.tir_label,
self.std,
merge_policy,
self.improvement_direction,
self.grouping_keys)
def GetBuildbotDataType(self, output_context):
if self._IsImportantGivenOutputIntent(output_context):
return 'default'
return 'unimportant'
def GetBuildbotValue(self):
return self.values
def GetRepresentativeNumber(self):
return _Mean(self.values)
def GetRepresentativeString(self):
return repr(self.values)
def IsMergableWith(self, that):
return (super(ListOfScalarValues, self).IsMergableWith(that) and
self.same_page_merge_policy == that.same_page_merge_policy)
@staticmethod
def GetJSONTypeName():
return 'list_of_scalar_values'
def AsDict(self):
d = super(ListOfScalarValues, self).AsDict()
d['values'] = self.values
d['std'] = self.std
if self.none_value_reason is not None:
d['none_value_reason'] = self.none_value_reason
return d
@staticmethod
def FromDict(value_dict, page_dict):
kwargs = value_module.Value.GetConstructorKwArgs(value_dict, page_dict)
kwargs['values'] = value_dict['values']
kwargs['std'] = value_dict['std']
if 'improvement_direction' in value_dict:
kwargs['improvement_direction'] = value_dict['improvement_direction']
if 'none_value_reason' in value_dict:
kwargs['none_value_reason'] = value_dict['none_value_reason']
return ListOfScalarValues(**kwargs)
@classmethod
def MergeLikeValuesFromSamePage(cls, values):
assert len(values) > 0
v0 = values[0]
if v0.same_page_merge_policy == value_module.PICK_FIRST:
return ListOfScalarValues(
v0.page, v0.name, v0.units,
values[0].values,
important=v0.important,
same_page_merge_policy=v0.same_page_merge_policy,
none_value_reason=v0.none_value_reason,
improvement_direction=v0.improvement_direction,
grouping_keys=v0.grouping_keys)
assert v0.same_page_merge_policy == value_module.CONCATENATE
return cls._MergeLikeValues(values, v0.page, v0.name, v0.tir_label,
v0.grouping_keys)
@classmethod
def MergeLikeValuesFromDifferentPages(cls, values):
assert len(values) > 0
v0 = values[0]
return cls._MergeLikeValues(values, None, v0.name, v0.tir_label,
v0.grouping_keys)
@classmethod
def _MergeLikeValues(cls, values, page, name, tir_label, grouping_keys):
v0 = values[0]
merged_values = []
list_of_samples = []
none_value_reason = None
pooled_std = None
for v in values:
if v.values is None:
merged_values = None
none_value_reason = none_values.MERGE_FAILURE_REASON
break
merged_values.extend(v.values)
list_of_samples.append(v.values)
if merged_values:
pooled_std = PooledStandardDeviation(
list_of_samples, list_of_variances=[v.variance for v in values])
return ListOfScalarValues(
page, name, v0.units,
merged_values,
important=v0.important,
description=v0.description,
tir_label=tir_label,
same_page_merge_policy=v0.same_page_merge_policy,
std=pooled_std,
none_value_reason=none_value_reason,
improvement_direction=v0.improvement_direction,
grouping_keys=grouping_keys)