Skip to content

Commit a032cc5

Browse files
committed
Merge branch 'feature/static-risk-traj' into feature/interpolated-trajectories
2 parents 7c2426c + 152f594 commit a032cc5

11 files changed

Lines changed: 508 additions & 184 deletions

File tree

-335 Bytes
Binary file not shown.

climada/entity/exposures/base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,9 @@ def __init__(
403403

404404
self.description = self._consolidate(meta, "description", description)
405405
self.ref_year = self._consolidate(meta, "ref_year", ref_year, DEF_REF_YEAR)
406+
407+
if geodata.shape[0] > 0:
408+
value_unit = self._consolidate(geodata.iloc[0], "value_unit", value_unit)
406409
self.value_unit = self._consolidate(
407410
meta, "value_unit", value_unit, DEF_VALUE_UNIT
408411
)

climada/entity/exposures/test/test_base.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def test__init__mda_in_kwargs(self):
182182
def test_read_raster_pass(self):
183183
"""from_raster"""
184184
exp = Exposures.from_raster(
185-
HAZ_DEMO_FL, window=Window(10, 20, 50, 60), attrs={"value_unit": "USD"}
185+
HAZ_DEMO_FL, window=Window(10, 20, 50, 60), attrs={"value_unit": "PKR"}
186186
)
187187
exp.check()
188188
self.assertTrue(u_coord.equal_crs(exp.crs, DEF_CRS))
@@ -204,7 +204,7 @@ def test_read_raster_pass(self):
204204
self.assertAlmostEqual(
205205
exp.gdf["value"].values.reshape((60, 50))[25, 12], 0.056825936
206206
)
207-
self.assertEqual(exp.value_unit, "USD")
207+
self.assertEqual(exp.value_unit, "PKR")
208208

209209
def test_assign_raster_pass(self):
210210
"""Test assign_centroids with raster hazard"""
@@ -429,16 +429,27 @@ def test_read_template_pass(self):
429429
exp_df = Exposures(df)
430430
# set metadata
431431
exp_df.ref_year = 2020
432-
exp_df.value_unit = "XSD"
432+
exp_df.value_unit = "PAK"
433433
exp_df.check()
434434

435+
def test_handling_unit_conflicts_pass(self):
436+
"""Check that the value_unit is correctly set when there are conflicting value_unit definitions in the data frame and the meta attribute."""
437+
df = pd.read_excel(ENT_TEMPLATE_XLS)
438+
exp_df = Exposures(df, meta={"value_unit": "XSD"}, value_unit="XSD")
439+
exp_df.check()
440+
self.assertEqual(exp_df.value_unit, "XSD")
441+
with self.assertRaises(ValueError) as cm:
442+
exp_df = Exposures(df, meta={"value_unit": "XSD"}, value_unit="PAK")
443+
with self.assertRaises(ValueError) as cm:
444+
exp_df = Exposures(df, meta={"value_unit": "PAK"}, value_unit="XSD")
445+
with self.assertRaises(ValueError) as cm:
446+
exp_df = Exposures(df, meta={"value_unit": "PAK"}, value_unit="PAK")
447+
435448
def test_io_hdf5_pass(self):
436449
"""write and read hdf5"""
437-
exp = Exposures(pd.read_excel(ENT_TEMPLATE_XLS), crs="epsg:32632")
438-
439-
# set metadata
440-
exp.ref_year = 2020
441-
exp.value_unit = "XSD"
450+
exp = Exposures(
451+
pd.read_excel(ENT_TEMPLATE_XLS), crs="epsg:32632", ref_year=2020
452+
)
442453

443454
# add another geometry column
444455
exp.data["geocol2"] = exp.data.geometry.copy(deep=True)

climada/trajectories/calc_risk_metrics.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def __init__(
160160
161161
"""
162162

163-
self._reset_impact_data()
163+
self._init_impact_data()
164164
self.snapshots = snapshots
165165
self.impact_computation_strategy = impact_computation_strategy
166166
self._date_idx = pd.DatetimeIndex(
@@ -182,8 +182,10 @@ def __init__(
182182
error_message = str(exc).lower()
183183
if "need at least one array to concatenate" in error_message:
184184
self._group_id = np.array([])
185+
else:
186+
raise
185187

186-
def _reset_impact_data(self):
188+
def _init_impact_data(self):
187189
"""Util method that resets computed data, for instance when
188190
changing the computation strategy.
189191
@@ -193,6 +195,8 @@ def _reset_impact_data(self):
193195
self._per_date_eai = None
194196
self._per_date_aai = None
195197

198+
_reset_impact_data = _init_impact_data
199+
196200
@property
197201
def impact_computation_strategy(self) -> ImpactComputationStrategy:
198202
"""The method used to calculate the impact from the (Haz,Exp,Vul)
@@ -204,7 +208,9 @@ def impact_computation_strategy(self) -> ImpactComputationStrategy:
204208
@impact_computation_strategy.setter
205209
def impact_computation_strategy(self, value, /):
206210
if not isinstance(value, ImpactComputationStrategy):
207-
raise ValueError("Not an impact computation strategy")
211+
raise ValueError(
212+
"The provided value is not an ImpactComputationStrategy object. See the trajectory module documentation for more information on how to define your own impact computation strategies."
213+
)
208214

209215
self._impact_computation_strategy = value
210216
self._reset_impact_data()
@@ -352,9 +358,12 @@ def calc_return_periods_metric(self, return_periods: list[int]) -> pd.DataFrame:
352358
return rp_df
353359

354360
def apply_measure(self, measure: Measure) -> "CalcRiskMetricsPoints":
355-
"""Creates a new `CalcRiskMetricsPoints` object with a measure.
361+
"""Creates a new `CalcRiskMetricsPoints` object by applying the effects
362+
of the given measure.
356363
357-
The given measure is applied to both snapshot of the risk period.
364+
The effects of the measure are applied to all the snapshots contained
365+
in the initial `CalcRiskMetricsPoints` and a new `CalcRiskMetricsPoints`
366+
containing the modified snapshots is returned.
358367
359368
Parameters
360369
----------
@@ -364,7 +373,7 @@ def apply_measure(self, measure: Measure) -> "CalcRiskMetricsPoints":
364373
Returns
365374
-------
366375
367-
CalcRiskPeriod
376+
CalcRiskMetricsPoints
368377
The risk period with given measure applied.
369378
370379
"""

climada/trajectories/static_trajectory.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,16 @@
5959

6060

6161
class StaticRiskTrajectory(RiskTrajectory):
62-
"""This class implements static risk trajectories, objects that
62+
"""This class implements static risk trajectories: objects that
6363
regroup impacts computations for multiple dates.
6464
65-
This class computes risk metrics over a series of snapshots,
66-
optionally applying risk discounting. It does not interpolate risk
67-
between the snapshot and only provides results for each snapshot.
65+
This class computes risk metrics over a series of `Snapshot` objects,
66+
optionally applying risk discounting, and offers access to the results
67+
in tidy formatted pandas DataFrames.
68+
69+
Contrary to InterpolatedRiskTrajectories, it does not interpolate risk
70+
between the snapshot and only provides results at each snapshot specific
71+
date.
6872
6973
"""
7074

@@ -115,7 +119,7 @@ def __init__(
115119
The discount rate to apply to future risk. Defaults to None.
116120
impact_computation_strategy: ImpactComputationStrategy, optional
117121
The method used to calculate the impact from the (Haz,Exp,Vul)
118-
of the two snapshots. Defaults to :class:`ImpactCalcComputation`.
122+
for each snapshot. Defaults to :class:`ImpactCalcComputation`.
119123
120124
"""
121125
super().__init__(
@@ -137,7 +141,11 @@ def impact_computation_strategy(self) -> ImpactComputationStrategy:
137141
@impact_computation_strategy.setter
138142
def impact_computation_strategy(self, value, /):
139143
if not isinstance(value, ImpactComputationStrategy):
140-
raise ValueError("Not an interpolation strategy")
144+
raise ValueError(
145+
"The provided impact computation strategy is not an ImpactComputationStrategy, "
146+
"please refer to the documentation to define your own strategies or stick to the "
147+
"default one"
148+
)
141149

142150
self._reset_metrics()
143151
self._risk_metrics_calculators.impact_computation_strategy = value
@@ -174,6 +182,15 @@ def _generic_metrics(
174182
A tidy formatted dataframe of the risk metric computed for the
175183
different snapshots.
176184
185+
Notes
186+
-----
187+
188+
The computation checks that there are no duplicated rows of results
189+
for the same tuples (Date, Group, Measure, Metric,
190+
[Coordinates for metrics on that level]) and takes the first row in
191+
this case.
192+
193+
177194
Raises
178195
------
179196
NotImplementedError
@@ -211,7 +228,12 @@ def _generic_metrics(
211228
# When more than 2 snapshots, there might be duplicated rows, we need to remove them.
212229
# Should not be the case in static trajectory, but in any case we really don't want
213230
# duplicated rows, which would mess up some dataframe manipulation down the road.
214-
tmp = tmp[~tmp.index.duplicated(keep="first")]
231+
if tmp.index.duplicated().any():
232+
LOGGER.warning(
233+
"Duplicated rows were found in the results. Will keep the first one."
234+
)
235+
tmp = tmp[~tmp.index.duplicated(keep="first")]
236+
215237
tmp = tmp.reset_index()
216238
if self._all_groups_name not in tmp[GROUP_COL_NAME].cat.categories:
217239
tmp[GROUP_COL_NAME] = tmp[GROUP_COL_NAME].cat.add_categories(
@@ -236,7 +258,8 @@ def eai_metrics(self, **kwargs) -> pd.DataFrame:
236258
Notes
237259
-----
238260
239-
This computation may become quite expensive for big areas with high resolution.
261+
This computation may become quite expensive for exposures with many points
262+
(e.g., big areas with high resolution).
240263
241264
"""
242265
metric_df = self._compute_metrics(

0 commit comments

Comments
 (0)