Skip to content

Commit

Permalink
change GravityModelResults to an abstract base class, updated run Mul…
Browse files Browse the repository at this point in the history
…tiTLD method to use correct class
  • Loading branch information
Kieran-Fishwick-TfN committed Jan 7, 2025
1 parent e407015 commit 64894c1
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 78 deletions.
193 changes: 118 additions & 75 deletions src/caf/distribute/gravity_model/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,9 @@


# # # CLASSES # # #
@dataclasses.dataclass
class GravityModelResults:
class GravityModelResults(abc.ABC):
"""A collection of results from a run of the Gravity Model.
Parameters
----------
cost_distribution:
The achieved cost distribution of the run.
cost_convergence:
The achieved cost convergence value of the run. If
`target_cost_distribution` is not set, then this should be 0.
This will be the same as calculating the convergence of
`cost_distribution` and `target_cost_distribution`.
value_distribution:
The achieved distribution of the given values (usually trip values
between different places).
target_cost_distribution:
The cost distribution the gravity model was aiming for during its run.
Expand All @@ -56,43 +40,74 @@ class GravityModelResults:
"""

cost_distribution: cost_utils.CostDistribution
"""The achieved cost distribution of the run."""
cost_convergence: float
"""The achieved cost convergence value of the run. If
`target_cost_distribution` is not set, then this should be 0.
This will be the same as calculating the convergence of
`cost_distribution` and `target_cost_distribution`.
"""
value_distribution: np.ndarray
"""The achieved distribution of the given values (usually trip values
between different places).
"""
cost_function: cost_functions.CostFunction
"""The cost function used in the gravity model run."""
cost_params: dict[str | int, Any]
"""The final/used cost parameters used by the cost function."""

def __init__(
self,
cost_distribution: cost_utils.CostDistribution,
cost_convergence: float,
value_distribution: np.ndarray,
cost_function: cost_functions.CostFunction,
cost_params: dict[str | int, Any],
) -> None:

@dataclasses.dataclass
class GravityModelCalibrateResults(GravityModelResults):
"""A collection of results from a run of the Gravity Model.
Parameters
----------
cost_distribution:
The achieved cost distribution of the run.
self.cost_distribution = cost_distribution
self.cost_convergence = cost_convergence
self.value_distribution = value_distribution
self.cost_function = cost_function
self.cost_params = cost_params

cost_convergence:
The achieved cost convergence value of the run. If
`target_cost_distribution` is not set, then this should be 0.
This will be the same as calculating the convergence of
`cost_distribution` and `target_cost_distribution`.
@abc.abstractmethod
def plot_distributions(self, truncate_last_bin: bool = False) -> figure.Figure:
"""Plot the distributions associated with the results.
value_distribution:
The achieved distribution of the given values (usually trip values
between different places).
Parameters
----------
truncate_last_bin : bool, optional
whether to truncate the graph to 1.2x the lower bin edge, by default False
"""

target_cost_distribution:
The cost distribution the gravity model was aiming for during its run.
@property
@abc.abstractmethod
def summary(self) -> pd.Series:
"""Summary of the results parameters as a series."""

cost_function:
The cost function used in the gravity model run.

cost_params:
The cost parameters used with the cost_function to achieve the results.
"""
class GravityModelCalibrateResults(GravityModelResults):
"""A collection of results from a calibration of the Gravity Model."""

# Targets
target_cost_distribution: cost_utils.CostDistribution
cost_function: cost_functions.CostFunction
cost_params: dict[str | int, Any]
"""The cost distribution the gravity model was aiming for during its run."""

def __init__(
self,
cost_distribution: cost_utils.CostDistribution,
cost_convergence: float,
value_distribution: np.ndarray,
target_cost_distribution: cost_utils.CostDistribution,
cost_function: cost_functions.CostFunction,
cost_params: dict[str | int, Any],
) -> None:

super().__init__(
cost_distribution, cost_convergence, value_distribution, cost_function, cost_params
)
self.target_cost_distribution = target_cost_distribution

def plot_distributions(self, truncate_last_bin: bool = False) -> figure.Figure:
"""Plot a comparison of the achieved and target distributions.
Expand Down Expand Up @@ -182,41 +197,20 @@ def summary(self) -> pd.Series:
return pd.Series(output_params)


@dataclasses.dataclass
class GravityModelRunResults(GravityModelResults):
"""A collection of results from a run of the Gravity Model.
Parameters
----------
cost_distribution:
The achieved cost distribution of the run.
cost_convergence:
The achieved cost convergence value of the run. If
`target_cost_distribution` is not set, then this should be 0.
This will be the same as calculating the convergence of
`cost_distribution` and `target_cost_distribution`.
value_distribution:
The achieved distribution of the given values (usually trip values
between different places).
target_cost_distribution:
If set, this will be the cost distribution the gravity
model was aiming for during its run.
cost_function:
If set, this will be the cost function used in the gravity model run.
cost_params:
If set, the cost parameters used with the cost_function to achieve
the results.
"""
"""A collection of results from a run of the Gravity Model."""

# Targets
target_cost_distribution: Optional[cost_utils.CostDistribution] = None
cost_function: Optional[cost_functions.CostFunction] = None
cost_params: Optional[dict[str, Any]] = None
def __init__(
self,
cost_distribution: cost_utils.CostDistribution,
cost_convergence: float,
value_distribution: np.ndarray,
cost_function: cost_functions.CostFunction,
cost_params: dict[int | str, Any],
) -> None:
super().__init__(
cost_distribution, cost_convergence, value_distribution, cost_function, cost_params
)

@property
def summary(self) -> pd.Series:
Expand All @@ -231,6 +225,55 @@ def summary(self) -> pd.Series:
"""
return pd.Series(self.cost_params)

def plot_distributions(self, truncate_last_bin: bool = False) -> figure.Figure:
"""Plot a comparison of the achieved and target distributions.
This method returns a matplotlib figure which can be saved or plotted
as the user decides.
Parameters
----------
truncate_last_bin : bool, optional
whether to truncate the graph to 1.2x the lower bin edge, by default False
Returns
-------
figure.Figure
the plotted distributions
Raises
------
ValueError
when the target and achieved distributions have different binning
"""

fig, ax = plt.subplots(figsize=(10, 6))

max_bin_edge = self.cost_distribution.max_vals
min_bin_edge = self.cost_distribution.min_vals
bin_centres = (max_bin_edge + min_bin_edge) / 2

ax.bar(
bin_centres,
self.cost_distribution.band_share_vals,
width=max_bin_edge - min_bin_edge,
label="Achieved Distribution",
color="blue",
alpha=0.7,
)

if truncate_last_bin:
top_min_bin = min_bin_edge.max()
ax.set_xlim(0, top_min_bin[-1] * 1.2)
fig.text(0.8, 0.025, f"final bin edge cut from {max_bin_edge.max()}", ha="center")

ax.set_xlabel("Cost")
ax.set_ylabel("Trips")
ax.set_title("Distribution Achieved")
ax.legend()

return fig


class GravityModelBase(abc.ABC):
"""Base Class for gravity models.
Expand Down
8 changes: 5 additions & 3 deletions src/caf/distribute/gravity_model/multi_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
# Local Imports
from caf.distribute import cost_functions, furness
from caf.distribute.gravity_model import core
from caf.distribute.gravity_model.core import GravityModelCalibrateResults
from caf.distribute.gravity_model.core import (
GravityModelCalibrateResults,
GravityModelRunResults,
)

# # # CONSTANTS # # #
LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -909,11 +912,10 @@ def run(
assert self.achieved_cost_dist is not None
results = {}
for i, dist in enumerate(distributions):
result_i = GravityModelCalibrateResults(
result_i = GravityModelRunResults(
cost_distribution=self.achieved_cost_dist[i],
cost_convergence=self.achieved_convergence[dist.name],
value_distribution=self.achieved_distribution[dist.zones],
target_cost_distribution=dist.cost_distribution,
cost_function=self.cost_function,
cost_params=self._cost_params_to_kwargs(
cost_args[i * params_len : i * params_len + params_len]
Expand Down

0 comments on commit 64894c1

Please sign in to comment.