Source code for sensai.util.aggregation
import collections
from typing import Hashable, Dict, Optional
from .string import ToStringMixin
[docs]class RelativeFrequencyCounter(ToStringMixin):
"""
Counts the absolute and relative frequency of an event
"""
def __init__(self):
self.num_total = 0
self.num_relevant = 0
[docs] def count(self, is_relevant_event) -> None:
"""
Adds to the count.
The nominator is incremented only if we are counting a relevant event.
The denominator is always incremented.
:param is_relevant_event: whether we are counting a relevant event
"""
self.num_total += 1
if is_relevant_event:
self.num_relevant += 1
def _tostring_object_info(self):
info = f"{self.num_relevant}/{self.num_total}"
if self.num_total > 0:
info += f", {100 * self.num_relevant / self.num_total:.2f}%"
return info
[docs] def add(self, relative_frequency_counter: "RelativeFrequencyCounter") -> None:
"""
Adds the counts of the given counter to this object
:param relative_frequency_counter: the counter whose data to add
"""
self.num_total += relative_frequency_counter.num_total
self.num_relevant += relative_frequency_counter.num_relevant
[docs] def get_relative_frequency(self) -> Optional[float]:
"""
:return: the relative frequency (between 0 and 1) or None if nothing was counted (0 events considered)
"""
if self.num_total == 0:
return None
return self.num_relevant / self.num_total
[docs]class DistributionCounter(ToStringMixin):
"""
Supports the counting of the frequencies with which (mutually exclusive) events occur
"""
def __init__(self):
self.counts = collections.defaultdict(self._zero)
self.total_count = 0
@staticmethod
def _zero():
return 0
[docs] def count(self, event: Hashable) -> None:
"""
Increments the count of the given event
:param event: the event/key whose count to increment, which must be hashable
"""
self.total_count += 1
self.counts[event] += 1
[docs] def get_distribution(self) -> Dict[Hashable, float]:
"""
:return: a dictionary mapping events (as previously passed to count) to their relative frequencies
"""
return {k: v/self.total_count for k, v in self.counts.items()}
def _tostring_object_info(self):
return ", ".join([f"{str(k)}: {v} ({v/self.total_count:.3f})" for k, v in self.counts.items()])
[docs]class WeightedMean(ToStringMixin):
"""
Computes a weighted mean of values
"""
def __init__(self):
self.weighted_value_sum = 0
self.weight_sum = 0
def _tostring_object_info(self) -> str:
return f"{self.weighted_value_sum / self.weight_sum}"
[docs] def add(self, value, weight=1) -> None:
"""
Adds the given value with the given weight to the calculation
:param value: the value
:param weight: the weight with which to consider the value
"""
self.weighted_value_sum += value * weight
self.weight_sum += weight
[docs] def get_weighted_mean(self):
"""
:return: the weighted mean of all values that have been added
"""
return self.weighted_value_sum / self.weight_sum