import attr
import pandas as pd
def func_repr(func):
return func.__name__ if hasattr(func, "__name__") else str("lambda")
def identity_relation(series: pd.Series) -> pd.Series:
return series
[docs]@attr.s(frozen=True)
class TypeRelation:
"""Relationship encoder between implementations of :class:`visions.types.type.VisionsBaseType`
Defines a one to one relationship between two :class:`visions.types.type.VisionsBaseType` implementations,
A and B, with respect to an underlying data series. In order to define a relationship we need
two methods:
- **is_relationship**, determines whether a series of type B can be alternatively represented as type A.
- **transform**, provides a mechanism to convert the series from B -> A.
For example, the series `pd.Series([1.0, 2.0, 3.0])` is encoded as a sequence of
floats but in reality they are all integers.
Examples:
>>> from visions.types import Integer, Float
>>> x = pd.Series([1.0, 2.0, 3.0])
>>> relation = TypeRelation(Integer, Float)
>>> relation.is_relation(x)
True
>>> relation.transform(x)
pd.Series([1, 2, 3])
"""
type = attr.ib()
related_type = attr.ib()
inferential = attr.ib()
transformer = attr.ib(repr=func_repr)
relationship = attr.ib(default=lambda x: False, repr=func_repr)
def is_relation(self, series: pd.Series) -> bool:
return self.relationship(series)
def transform(self, series: pd.Series) -> pd.Series:
return self.transformer(series)
def __str__(self):
return f"{self.related_type}->{self.type}"
[docs]@attr.s(frozen=True)
class IdentityRelation(TypeRelation):
relationship = attr.ib(repr=func_repr)
transformer = attr.ib(default=identity_relation, repr=func_repr)
inferential = attr.ib(default=False)
@relationship.default
def make_relationship(self):
return self.type.__contains__
[docs]@attr.s(frozen=True)
class InferenceRelation(TypeRelation):
relationship = attr.ib(repr=func_repr)
inferential = attr.ib(default=True)
@relationship.default
def make_relationship(self):
return self.type.__contains__