Skip to content

Commit 25d0d0a

Browse files
committed
fix,doc(interpolation): minor improvements and fixes
1 parent c6e147e commit 25d0d0a

File tree

2 files changed

+53
-41
lines changed

2 files changed

+53
-41
lines changed

climada/trajectories/interpolation.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def exponential_interp_imp_mat(mat_start, mat_end, interpolation_range, rate) ->
5050
`mat_start` and `mat_end`.
5151
"""
5252
# Convert matrices to logarithmic domain
53+
if rate <= 0:
54+
raise ValueError("Rate for exponential interpolation must be positive")
55+
5356
mat_start = mat_start.copy()
5457
mat_end = mat_end.copy()
5558
mat_start.data = np.log(mat_start.data + np.finfo(float).eps) / np.log(rate)
@@ -65,8 +68,16 @@ def exponential_interp_imp_mat(mat_start, mat_end, interpolation_range, rate) ->
6568
return res
6669

6770

68-
def linear_interp_arrays(arr_start, arr_end, interpolation_range):
69-
"""Perform linear interpolation between two arrays (of a scalar metric) over an interpolation range."""
71+
def linear_interp_arrays(arr_start, arr_end):
72+
"""Perform linear interpolation between two arrays of `n` dates of one or multiple scalar metrics.
73+
74+
Returns a `n` sized arrays where the values linearly change from `arr_start` to `arr_end` over the `n` dates.
75+
"""
76+
if arr_start.shape != arr_end.shape:
77+
raise ValueError(
78+
f"Cannot interpolate arrays of different shapes: {arr_start.shape} and {arr_end.shape}."
79+
)
80+
interpolation_range = arr_start.shape[0]
7081
prop1 = np.linspace(0, 1, interpolation_range)
7182
prop0 = 1 - prop1
7283
if arr_start.ndim > 1:
@@ -75,32 +86,35 @@ def linear_interp_arrays(arr_start, arr_end, interpolation_range):
7586
return np.multiply(arr_start, prop0) + np.multiply(arr_end, prop1)
7687

7788

78-
def exponential_interp_arrays(arr_start, arr_end, interpolation_range, rate):
79-
"""Perform exponential interpolation between two arrays (of a scalar metric) over an interpolation range with a growth rate `rate`."""
89+
def exponential_interp_arrays(arr_start, arr_end, rate):
90+
"""Perform exponential interpolation between two arrays of `n` dates of one or multiple scalar metrics.
91+
92+
Returns a `n` sized arrays where the values exponentially change from `arr_start` to `arr_end` over the `n` dates.
93+
"""
94+
95+
if rate <= 0:
96+
raise ValueError("Rate for exponential interpolation must be positive")
97+
98+
if arr_start.shape != arr_end.shape:
99+
raise ValueError(
100+
f"Cannot interpolate arrays of different shapes: {arr_start.shape} and {arr_end.shape}."
101+
)
102+
interpolation_range = arr_start.shape[0]
103+
80104
prop1 = np.linspace(0, 1, interpolation_range)
81105
prop0 = 1 - prop1
82106
if arr_start.ndim > 1:
83107
prop0, prop1 = prop0.reshape(-1, 1), prop1.reshape(-1, 1)
84108

85109
return np.exp(
86110
(
87-
np.multiply(np.log(arr_start) / np.log(rate), prop0)
88-
+ np.multiply(np.log(arr_end) / np.log(rate), prop1)
111+
np.multiply(np.log(arr_start + np.finfo(float).eps) / np.log(rate), prop0)
112+
+ np.multiply(np.log(arr_end + np.finfo(float).eps) / np.log(rate), prop1)
89113
)
90114
* np.log(rate)
91115
)
92116

93117

94-
def logarithmic_interp_arrays(arr_start, arr_end, interpolation_range):
95-
"""Perform logarithmic (natural logarithm) interpolation between two arrays (of a scalar metric) over an interpolation range."""
96-
prop1 = np.logspace(0, 1, interpolation_range)
97-
prop0 = 1 - prop1
98-
if arr_start.ndim > 1:
99-
prop0, prop1 = prop0.reshape(-1, 1), prop1.reshape(-1, 1)
100-
101-
return np.multiply(arr_start, prop0) + np.multiply(arr_end, prop1)
102-
103-
104118
class InterpolationStrategyBase(ABC):
105119
exposure_interp: Callable
106120
hazard_interp: Callable
@@ -126,17 +140,11 @@ def interp_exposure_dim(
126140

127141
return res
128142

129-
def interp_hazard_dim(
130-
self, metric_0, metric_1, interpolation_range: int, **kwargs
131-
) -> np.ndarray:
132-
return self.hazard_interp(metric_0, metric_1, interpolation_range, **kwargs)
143+
def interp_hazard_dim(self, metric_0, metric_1, **kwargs) -> np.ndarray:
144+
return self.hazard_interp(metric_0, metric_1, **kwargs)
133145

134-
def interp_vulnerability_dim(
135-
self, metric_0, metric_1, interpolation_range: int, **kwargs
136-
) -> np.ndarray:
137-
return self.vulnerability_interp(
138-
metric_0, metric_1, interpolation_range, **kwargs
139-
)
146+
def interp_vulnerability_dim(self, metric_0, metric_1, **kwargs) -> np.ndarray:
147+
return self.vulnerability_interp(metric_0, metric_1, **kwargs)
140148

141149

142150
class InterpolationStrategy(InterpolationStrategyBase):

climada/trajectories/riskperiod.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -671,13 +671,13 @@ def calc_eai(self):
671671
self.per_date_eai_H1V1,
672672
)
673673
per_date_eai_V0 = self.interpolation_strategy.interp_hazard_dim(
674-
per_date_eai_H0V0, per_date_eai_H1V0, self.time_points
674+
per_date_eai_H0V0, per_date_eai_H1V0
675675
)
676676
per_date_eai_V1 = self.interpolation_strategy.interp_hazard_dim(
677-
per_date_eai_H0V1, per_date_eai_H1V1, self.time_points
677+
per_date_eai_H0V1, per_date_eai_H1V1
678678
)
679679
per_date_eai = self.interpolation_strategy.interp_vulnerability_dim(
680-
per_date_eai_V0, per_date_eai_V1, self.time_points
680+
per_date_eai_V0, per_date_eai_V1
681681
)
682682
return per_date_eai
683683

@@ -690,6 +690,7 @@ def calc_eai_gdf(self):
690690
eai_gdf["coord_id"] = eai_gdf.index
691691
eai_gdf = eai_gdf.merge(df, on="coord_id")
692692
eai_gdf = eai_gdf.rename(columns={"group_id": "group"})
693+
eai_gdf["group"] = eai_gdf["group"].astype("category")
693694
eai_gdf["metric"] = "eai"
694695
eai_gdf["measure"] = self.measure.name if self.measure else "no_measure"
695696
return eai_gdf
@@ -720,14 +721,17 @@ def calc_aai_per_group_metric(self):
720721
"Group id are changing between present and future snapshot. Per group AAI will be linearly interpolated."
721722
)
722723
eai_fut_groups = self.eai_gdf.copy()
723-
eai_fut_groups.index = self._group_id_E1
724-
aai_fut_groups = (
725-
eai_fut_groups.groupby(["date", "group"], as_index=False)["risk"]
726-
.sum()
727-
.values()
724+
eai_fut_groups["group"] = pd.Categorical(
725+
np.tile(self._group_id_E1, len(self.date_idx)),
726+
categories=np.unique(
727+
np.concatenate([self._group_id_E0, self._group_id_E1])
728+
),
728729
)
730+
aai_fut_groups = eai_fut_groups.groupby(["date", "group"], as_index=False)[
731+
"risk"
732+
].sum()
729733
aai_per_group_df["risk"] = linear_interp_arrays(
730-
aai_per_group_df["risk"], aai_fut_groups, self.time_points
734+
aai_per_group_df["risk"].values, aai_fut_groups["risk"].values
731735
)
732736

733737
aai_per_group_df["metric"] = "aai"
@@ -745,13 +749,13 @@ def calc_return_periods_metric(self, return_periods):
745749
self.per_date_return_periods_H1V1(return_periods),
746750
)
747751
per_date_rp_V0 = self.interpolation_strategy.interp_hazard_dim(
748-
per_date_rp_H0V0, per_date_rp_H1V0, self.time_points
752+
per_date_rp_H0V0, per_date_rp_H1V0
749753
)
750754
per_date_rp_V1 = self.interpolation_strategy.interp_hazard_dim(
751-
per_date_rp_H0V1, per_date_rp_H1V1, self.time_points
755+
per_date_rp_H0V1, per_date_rp_H1V1
752756
)
753757
per_date_rp = self.interpolation_strategy.interp_vulnerability_dim(
754-
per_date_rp_V0, per_date_rp_V1, self.time_points
758+
per_date_rp_V0, per_date_rp_V1
755759
)
756760
rp_df = pd.DataFrame(
757761
index=self.date_idx, columns=return_periods, data=per_date_rp
@@ -763,11 +767,11 @@ def calc_return_periods_metric(self, return_periods):
763767
return rp_df
764768

765769
def calc_risk_components_metric(self):
766-
per_date_aai_V0 = self.interpolation_strategy.interp_vulnerability_dim(
767-
self.per_date_aai_H0V0, self.per_date_aai_H1V0, self.time_points
770+
per_date_aai_V0 = self.interpolation_strategy.interp_hazard_dim(
771+
self.per_date_aai_H0V0, self.per_date_aai_H1V0
768772
)
769773
per_date_aai_H0 = self.interpolation_strategy.interp_vulnerability_dim(
770-
self.per_date_aai_H0V0, self.per_date_aai_H0V1, self.time_points
774+
self.per_date_aai_H0V0, self.per_date_aai_H0V1
771775
)
772776
df = pd.DataFrame(
773777
{

0 commit comments

Comments
 (0)