def _welford(): """Welford's method of calculating the running variance. Consume values and return running estimates of (n, M2) where: n = number of data points seen so far M2 = the second moment about the mean = sum( (x-m)**2 ) where m = mean of the x seen so far. """ # Note: for better results, use this on the residues (x - m) instead # of the raw x values, where m equals the mean of the data. M2_partials = [] x = (yield None) m = x # First estimate of the mean is the first value. n = 1 while True: delta = x - m m += delta/n # Update the mean. stats.add_partial(delta*(x-m), M2_partials) # Update the 2nd moment. M2 = _sum(M2_partials) assert M2 >= 0.0 x = (yield (n, M2)) n += 1
def value(self): return _sum(self.partials)
def sum(data, start=0): """sum(data [, start]) -> value Return a high-precision sum of the given numeric data. If optional argument ``start`` is given, it is added to the total. If ``data`` is empty, ``start`` (defaulting to 0) is returned. Examples -------- >>> sum([3, 2.25, 4.5, -0.5, 1.0], 0.75) 11.0 Float sums are calculated using high-precision floating point arithmetic that can avoid some sources of round-off error: >>> sum([1e50, 1, -1e50] * 1000) # Built-in sum returns zero. 1000.0 Fractions and Decimals are also supported: >>> from fractions import Fraction as F >>> sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)]) Fraction(63, 20) Decimal sums honour the context: >>> import decimal >>> D = decimal.Decimal >>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")] >>> sum(data) Decimal('0.6963') >>> with decimal.localcontext( ... decimal.Context(prec=2, rounding=decimal.ROUND_DOWN)): ... sum(data) Decimal('0.68') Limitations ----------- ``sum`` supports mixed arithmetic with the following limitations: - mixing Fractions and Decimals raises TypeError; - mixing floats with either Fractions or Decimals coerces to float, which may lose precision; - complex numbers are not supported. These limitations may change without notice in future versions. """ if not isinstance(start, numbers.Number): raise TypeError('sum only accepts numbers') total = start data = iter(data) x = None if not isinstance(total, float): # Non-float sum. If we find a float, we exit this loop and continue # with the float code below. Until that happens, we keep adding. for x in data: if isinstance(x, float): # Convert running total to a float. See comment below for # why we do it this way. total = type(total).__float__(total) break total += x else: # No break, so we're done. return total # High-precision float sum. assert isinstance(total, float) partials = [] add_partial(total, partials) if x is not None: add_partial(x, partials) for x in data: try: # Don't call float() directly, as that converts strings and we # don't want that. Also, like all dunder methods, we should call # __float__ on the class, not the instance. x = type(x).__float__(x) except OverflowError: x = float('inf') if x > 0 else float('-inf') add_partial(x, partials) return _sum(partials)