types.py
1 """Additional types, classes, dataclasses, etc.""" 2 3 from typing import Any 4 from typing import ClassVar 5 from typing import Dict 6 from typing import Optional 7 from typing import Union 8 9 from evidently.pydantic_utils import ExcludeNoneMixin 10 from evidently.pydantic_utils import FrozenBaseModel 11 12 Numeric = Union[float, int] 13 14 # type for distributions - list of tuples (value, count) 15 ColumnDistribution = Dict[Any, Numeric] 16 17 18 class ApproxValue(FrozenBaseModel, ExcludeNoneMixin): 19 """Class for approximate scalar value calculations""" 20 21 class Config: 22 smart_union = True 23 24 DEFAULT_RELATIVE: ClassVar = 1e-6 25 DEFAULT_ABSOLUTE: ClassVar = 1e-12 26 27 value: Numeric 28 relative: Numeric 29 absolute: Numeric 30 31 def __init__(self, value: Numeric, relative: Optional[Numeric] = None, absolute: Optional[Numeric] = None): 32 if relative is not None and relative <= 0: 33 raise ValueError("Relative value for approx should be greater than 0") 34 35 if relative is None: 36 relative = self.DEFAULT_RELATIVE 37 38 if absolute is None: 39 absolute = self.DEFAULT_ABSOLUTE 40 41 super().__init__(value=value, relative=relative, absolute=absolute) 42 43 @property 44 def tolerance(self) -> Numeric: 45 relative_value = abs(self.value) * self.relative 46 return max(relative_value, self.absolute) 47 48 def __format__(self, format_spec): 49 return f"{format(self.value, format_spec)} ± {format(self.tolerance, format_spec)}" 50 51 def __repr__(self): 52 return f"{self.value} ± {self.tolerance}" 53 54 def __eq__(self, other): 55 tolerance = self.tolerance 56 return (self.value - tolerance) <= other <= (self.value + tolerance) 57 58 def __lt__(self, other): 59 return self.value + self.tolerance < other 60 61 def __le__(self, other): 62 return self.value - self.tolerance <= other 63 64 def __gt__(self, other): 65 return self.value - self.tolerance > other 66 67 def __ge__(self, other): 68 return self.value + self.tolerance >= other 69 70 71 NumericApprox = Union[int, float, ApproxValue]