Skip to content

Commit

Permalink
Merge pull request #76 from e10v/dev
Browse files Browse the repository at this point in the history
Format power as percent value
  • Loading branch information
e10v committed Jul 13, 2024
2 parents e833c56 + d862de6 commit 3ff51d3
Show file tree
Hide file tree
Showing 2 changed files with 236 additions and 234 deletions.
270 changes: 135 additions & 135 deletions src/tea_tasting/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,141 @@ def format_num(
return result


def get_and_format_num(data: dict[str, Any], key: str) -> str:
"""Get and format dictionary value.
Args:
data: Dictionary.
key: Key.
Returns:
Formatted value.
"""
if key.endswith("_ci"):
ci_lower = get_and_format_num(data, key + "_lower")
ci_upper = get_and_format_num(data, key + "_upper")
return f"[{ci_lower}, {ci_upper}]"

val = data.get(key)
if not isinstance(val, float | int | None):
return str(val)

sig, pct = (2, True) if key.startswith("rel_") or key == "power" else (3, False)
return format_num(val, sig=sig, pct=pct)


class PrettyDictsMixin(abc.ABC):
"""Pretty representation of a sequence of dictionaries."""
default_keys: Sequence[str]

@abc.abstractmethod
def to_dicts(self) -> Sequence[dict[str, Any]]:
"""Convert the object to a sequence of dictionaries."""

def to_pandas(self) -> pd.DataFrame:
"""Convert the object to a Pandas DataFrame."""
return pd.DataFrame.from_records(self.to_dicts())

def to_pretty(
self,
keys: Sequence[str] | None = None,
formatter: Callable[[dict[str, Any], str], str] = get_and_format_num,
) -> pd.DataFrame:
"""Convert the object to a Pandas Dataframe with formatted values.
Args:
keys: Keys to convert. If a key is not defined in the dictionary
it's assumed to be `None`.
formatter: Custom formatter function. It should accept a dictionary
of metric result attributes and an attribute name, and return
a formatted attribute value.
Returns:
Pandas Dataframe with formatted values.
"""
if keys is None:
keys = self.default_keys
return pd.DataFrame.from_records(
{key: formatter(data, key) for key in keys}
for data in self.to_dicts()
)

def to_string(
self,
keys: Sequence[str] | None = None,
formatter: Callable[[dict[str, Any], str], str] = get_and_format_num,
) -> str:
"""Convert the object to a string.
Args:
keys: Keys to convert. If a key is not defined in the dictionary
it's assumed to be `None`.
formatter: Custom formatter function. It should accept a dictionary
of metric result attributes and an attribute name, and return
a formatted attribute value.
Returns:
A table with results rendered as string.
"""
if keys is None:
keys = self.default_keys
return self.to_pretty(keys, formatter).to_string(index=False)

def to_html(
self,
keys: Sequence[str] | None = None,
formatter: Callable[[dict[str, Any], str], str] = get_and_format_num,
) -> str:
"""Convert the object to HTML.
Args:
keys: Keys to convert. If a key is not defined in the dictionary
it's assumed to be `None`.
formatter: Custom formatter function. It should accept a dictionary
of metric result attributes and an attribute name, and return
a formatted attribute value.
Returns:
A table with results rendered as HTML.
"""
if keys is None:
keys = self.default_keys
return self.to_pretty(keys, formatter).to_html(index=False)

def __str__(self) -> str:
"""Object string representation."""
return self.to_string()

def _repr_html_(self) -> str:
"""Object HTML representation."""
return self.to_html()


class ReprMixin:
"""A mixin class that provides a method for generating a string representation.
Representation string is generated based on parameters values saved in attributes.
"""
@classmethod
def _get_param_names(cls) -> Iterator[str]:
if cls.__init__ is object.__init__:
return
init_signature = inspect.signature(cls.__init__)

for p in init_signature.parameters.values():
if p.kind == p.VAR_POSITIONAL:
raise RuntimeError(
"There should not be positional parameters in the __init__.")
if p.name != "self" and p.kind != p.VAR_KEYWORD:
yield p.name

def __repr__(self) -> str:
"""Object representation."""
params = {p: getattr(self, p) for p in self._get_param_names()}
params_repr = ", ".join(f"{k}={v!r}" for k, v in params.items())
return f"{self.__class__.__name__}({params_repr})"


def div(
numer: float | int,
denom: float | int,
Expand Down Expand Up @@ -339,138 +474,3 @@ def numeric(
return Int(value, fill_zero_div)
except ValueError:
return Float(value, fill_zero_div)


class ReprMixin:
"""A mixin class that provides a method for generating a string representation.
Representation string is generated based on parameters values saved in attributes.
"""
@classmethod
def _get_param_names(cls) -> Iterator[str]:
if cls.__init__ is object.__init__:
return
init_signature = inspect.signature(cls.__init__)

for p in init_signature.parameters.values():
if p.kind == p.VAR_POSITIONAL:
raise RuntimeError(
"There should not be positional parameters in the __init__.")
if p.name != "self" and p.kind != p.VAR_KEYWORD:
yield p.name

def __repr__(self) -> str:
"""Object representation."""
params = {p: getattr(self, p) for p in self._get_param_names()}
params_repr = ", ".join(f"{k}={v!r}" for k, v in params.items())
return f"{self.__class__.__name__}({params_repr})"


def get_and_format_num(data: dict[str, Any], key: str) -> str:
"""Get and format dictionary value.
Args:
data: Dictionary.
key: Key.
Returns:
Formatted value.
"""
if key.endswith("_ci"):
ci_lower = get_and_format_num(data, key + "_lower")
ci_upper = get_and_format_num(data, key + "_upper")
return f"[{ci_lower}, {ci_upper}]"

val = data.get(key)
if not isinstance(val, float | int | None):
return str(val)

sig, pct = (2, True) if key.startswith("rel_") else (3, False)
return format_num(val, sig=sig, pct=pct)


class PrettyDictsMixin(abc.ABC):
"""Pretty representation of a sequence of dictionaries."""
default_keys: Sequence[str]

@abc.abstractmethod
def to_dicts(self) -> Sequence[dict[str, Any]]:
"""Convert the object to a sequence of dictionaries."""

def to_pandas(self) -> pd.DataFrame:
"""Convert the object to a Pandas DataFrame."""
return pd.DataFrame.from_records(self.to_dicts())

def to_pretty(
self,
keys: Sequence[str] | None = None,
formatter: Callable[[dict[str, Any], str], str] = get_and_format_num,
) -> pd.DataFrame:
"""Convert the object to a Pandas Dataframe with formatted values.
Args:
keys: Keys to convert. If a key is not defined in the dictionary
it's assumed to be `None`.
formatter: Custom formatter function. It should accept a dictionary
of metric result attributes and an attribute name, and return
a formatted attribute value.
Returns:
Pandas Dataframe with formatted values.
"""
if keys is None:
keys = self.default_keys
return pd.DataFrame.from_records(
{key: formatter(data, key) for key in keys}
for data in self.to_dicts()
)

def to_string(
self,
keys: Sequence[str] | None = None,
formatter: Callable[[dict[str, Any], str], str] = get_and_format_num,
) -> str:
"""Convert the object to a string.
Args:
keys: Keys to convert. If a key is not defined in the dictionary
it's assumed to be `None`.
formatter: Custom formatter function. It should accept a dictionary
of metric result attributes and an attribute name, and return
a formatted attribute value.
Returns:
A table with results rendered as string.
"""
if keys is None:
keys = self.default_keys
return self.to_pretty(keys, formatter).to_string(index=False)

def to_html(
self,
keys: Sequence[str] | None = None,
formatter: Callable[[dict[str, Any], str], str] = get_and_format_num,
) -> str:
"""Convert the object to HTML.
Args:
keys: Keys to convert. If a key is not defined in the dictionary
it's assumed to be `None`.
formatter: Custom formatter function. It should accept a dictionary
of metric result attributes and an attribute name, and return
a formatted attribute value.
Returns:
A table with results rendered as HTML.
"""
if keys is None:
keys = self.default_keys
return self.to_pretty(keys, formatter).to_html(index=False)

def __str__(self) -> str:
"""Object string representation."""
return self.to_string()

def _repr_html_(self) -> str:
"""Object HTML representation."""
return self.to_html()
Loading

0 comments on commit 3ff51d3

Please sign in to comment.