diff --git a/CHANGELOG.md b/CHANGELOG.md index 55254359c..eaab28ab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Simulation user guide - Example for transfer learning backtest utility - `pyupgrade` pre-commit hook +- Better human readable `__str__` representation of objective and targets ### Changed - More detailed and sophisticated search space user guide diff --git a/baybe/objective.py b/baybe/objective.py index 79d500acb..d8574477c 100644 --- a/baybe/objective.py +++ b/baybe/objective.py @@ -51,6 +51,21 @@ class Objective(SerialMixin): ) """The function used to combine the different targets.""" + def __str__(self) -> str: + start_bold = "\033[1m" + end_bold = "\033[0m" + + # Convert the targets list to a dataframe to have a tabular output + targets_list = [target.summary() for target in self.targets] + targets_df = pd.DataFrame(targets_list) + targets_df["Weight"] = self.weights + + objective_str = f"""{start_bold}Objective{end_bold} + \n{start_bold}Mode: {end_bold}{self.mode} + \n{start_bold}Targets {end_bold}\n{targets_df} + \n{start_bold}Combine Function: {end_bold}{self.combine_func}""" + return objective_str.replace("\n", "\n ") + @weights.default def _default_weights(self) -> list[float]: """Create the default weights.""" diff --git a/baybe/parameters/base.py b/baybe/parameters/base.py index 7f0747515..a968ed026 100644 --- a/baybe/parameters/base.py +++ b/baybe/parameters/base.py @@ -52,6 +52,9 @@ def is_in_range(self, item: Any) -> bool: def summary(self) -> dict: """Return a custom summarization of the parameter.""" + def __str__(self) -> str: + return str(self.summary()) + @define(frozen=True, slots=False) class DiscreteParameter(Parameter, ABC): diff --git a/baybe/searchspace/core.py b/baybe/searchspace/core.py index 8d3f8ce8a..92b9d6f94 100644 --- a/baybe/searchspace/core.py +++ b/baybe/searchspace/core.py @@ -71,10 +71,16 @@ class SearchSpace(SerialMixin): def __str__(self) -> str: start_bold = "\033[1m" end_bold = "\033[0m" - searchspace_str = f"""{start_bold}Search Space{end_bold} - \n{start_bold}Search Space Type: {end_bold}{self.type.name} - \n{self.discrete} - \n{self.continuous}""" + head_str = f"""{start_bold}Search Space{end_bold} + \n{start_bold}Search Space Type: {end_bold}{self.type.name}""" + + # Check the sub space size to avoid adding unwanted break lines + # if the sub space is empty + discrete_str = f"\n\n{self.discrete}" if not self.discrete.is_empty else "" + continuous_str = ( + f"\n\n{self.continuous}" if not self.continuous.is_empty else "" + ) + searchspace_str = f"{head_str}{discrete_str}{continuous_str}" return searchspace_str.replace("\n", "\n ").replace("\r", "\r ") def __attrs_post_init__(self): diff --git a/baybe/targets/base.py b/baybe/targets/base.py index f3b85ef5e..612d15f1e 100644 --- a/baybe/targets/base.py +++ b/baybe/targets/base.py @@ -37,6 +37,13 @@ def transform(self, data: pd.DataFrame) -> pd.DataFrame: A dataframe containing the transformed data. """ + @abstractmethod + def summary(self) -> dict: + """Return a custom summarization of the target.""" + + def __str__(self) -> str: + return str(self.summary()) + def _add_missing_type_hook(hook): """Adjust the structuring hook such that it auto-fills missing target types. diff --git a/baybe/targets/numerical.py b/baybe/targets/numerical.py index 6c6aece98..1d44eec95 100644 --- a/baybe/targets/numerical.py +++ b/baybe/targets/numerical.py @@ -146,3 +146,15 @@ def transform(self, data: pd.DataFrame) -> pd.DataFrame: # noqa: D102 transformed = data.copy() return transformed + + def summary(self) -> dict: # noqa: D102 + # See base class. + target_dict = dict( + Type=self.__class__.__name__, + Name=self.name, + Mode=self.mode.name, + Lower_Bound=self.bounds.lower, + Upper_Bound=self.bounds.upper, + Transformation=self.transformation.name if self.transformation else "None", + ) + return target_dict