# -*- coding: utf-8 -*-
"""
lantz.stats
~~~~~~~~~~~
Implements an statistical accumulator
:copyright: 2015 by Lantz Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from collections import namedtuple
#: Data structure
Stats = namedtuple('Stats', 'last count mean std min max')
[docs]def stats(state):
"""Return the statistics for given state.
:param state: state
:type state: RunningState
:return: statistics
:rtype: Stats named tuple
"""
if not state.count:
return Stats(0, 0, 0, 0, 0, 0)
mean = state.sum / state.count
std = (float(state.sum2 - 2.0 * state.sum * mean + state.count * mean ** 2.0) / float(state.count)) ** 0.5
return Stats(state.last, state.count,
mean, std, state.min, state.max)
[docs]class RunningState(object):
"""Accumulator for events.
:param value: first value to add.
"""
def __init__(self, value=None):
if value is not None:
self.add(value)
def __getattr__(self, key):
if key in ('last', 'count', 'sum', 'sum2'):
return 0
if key == 'min':
return float('inf')
if key == 'max':
return float('-inf')
raise AttributeError('{} is not a valid attribute of RunningState'.format(key))
[docs] def add(self, value):
"""Add to the accumulator.
:param value: value to be added.
"""
self.last = value
self.count += 1
self.sum += value
self.sum2 += value * value
self.min = min(self.min, value)
self.max = max(self.max, value)
[docs]class RunningStats(dict):
"""Accumulator for categorized event statistics.
"""
[docs] def add(self, key, value):
"""Add an event to a given accumulator.
:param key: category to which the event should be added.
:param value: value of the event.
"""
if key in self:
super().__getitem__(key).add(value)
else:
super().__setitem__(key, RunningState(value))
[docs] def stats(self, key):
"""Return the statistics for the current accumulator.
:rtype: Stats.
"""
return stats(super().__getitem__(key))