Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7da50f1
Return impactForecast object in _return_impact
luseverin Dec 8, 2025
bd3502f
Return impactForecast object in _return_empty
luseverin Dec 8, 2025
dfa0198
Add full impactcalc test for impactForecast
luseverin Dec 8, 2025
59c0e5b
Correct mistakes in _return_empty and _return_impact
luseverin Dec 8, 2025
4b5ae95
Raise value error when computing impact with impact forecast without …
luseverin Dec 8, 2025
9a516e1
Cosmetics: Improve error message, move test to own class
Dec 8, 2025
d571bb7
Merge branch 'forecast-class' into impactCalc_block_nonsense_attrs
luseverin Dec 9, 2025
6fe29f0
Merge branch 'impactCalc_return_impactForecast' into impactCalc_block…
luseverin Dec 9, 2025
899d8f0
add test to check that eai_exp and aai_agg are nan for forecasts
luseverin Dec 9, 2025
db32170
Write nans for eai_exp and aai_agg when forecast is used
luseverin Dec 9, 2025
d2f035f
update tests using pytest
luseverin Dec 9, 2025
c10a4b3
Fix error in test fixtures
luseverin Dec 9, 2025
d3a5642
Returns nans for eai_exp and aai_agg when exposures is empty
luseverin Dec 9, 2025
d43a46c
add warning when at_event is used with forecast
luseverin Dec 9, 2025
e197566
Update ImpactCalc tests for forecasts
peanutfun Dec 9, 2025
0a324b2
Merge branch 'impactCalc_return_impactForecast' of https://github.com…
peanutfun Dec 9, 2025
554cfc8
Review ImpactCalc forecast handling
peanutfun Dec 9, 2025
30ed14d
Block local_exceedance_impact
luseverin Dec 9, 2025
f3ab44a
Fix bug in test
peanutfun Dec 9, 2025
612a53c
Merge branch 'impactCalc_return_impactForecast' of https://github.com…
peanutfun Dec 9, 2025
9e4f2bb
Block return_period and exceedance_freq_curve
luseverin Dec 9, 2025
727357e
Log warning for at_event getter
luseverin Dec 9, 2025
0bc5846
Add mean max min methods to impact forecast and tests
luseverin Dec 9, 2025
a55ab3b
Merge branch 'forecast-class' into implement_mean_min_max
luseverin Dec 9, 2025
0aad2a5
Reduce lead time and member and update test
luseverin Dec 10, 2025
102f9be
Update docstrings
luseverin Dec 10, 2025
dfce16f
Remove useless comments
luseverin Dec 10, 2025
0b6e1e4
Add min max mean for hazard forecast
luseverin Dec 10, 2025
3fc25af
Correct some mistakes in mean min max on hazard forecast
luseverin Dec 10, 2025
a4fe3b0
Add tests for hazard forecast mean min max
luseverin Dec 10, 2025
4a80344
Merge branch 'implement_mean_min_max' into implement_quantiles
luseverin Dec 10, 2025
2feb7d2
Add quantiles and median for hazard forecast
luseverin Dec 10, 2025
e099868
Set reduced date to 0 and cosmetic changes
luseverin Dec 10, 2025
23660de
Set reduced date to 0 in impact forecast
luseverin Dec 10, 2025
efc1ac2
Merge branch 'implement_mean_min_max' into implement_quantiles
luseverin Dec 10, 2025
ab5b551
Correct quantile computation with np.quantile and toarray
luseverin Dec 10, 2025
b11e9c5
Add test for quantile calc
luseverin Dec 10, 2025
64149df
Simplify code and tests
peanutfun Dec 10, 2025
3b4048e
Add quantile and median for impact forecast
luseverin Dec 10, 2025
df20622
Merge branch 'implement_mean_min_max' into implement_quantiles
luseverin Dec 10, 2025
66ee290
Merge branch 'forecast-class' into implement_quantiles
luseverin Dec 10, 2025
b9524d6
Merge branch 'forecast-class' into implement_quantiles
peanutfun Dec 10, 2025
35cb0fa
Remove accidentally doubled members
peanutfun Dec 10, 2025
2e3c26e
Review
peanutfun Dec 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions climada/engine/impact_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,56 @@ def select(
coord_exp=coord_exp,
reset_frequency=reset_frequency,
)

def _quantile(self, q: float, event_name: str | None = None):
"""
Reduce the impact matrix and at_event of an ImpactForecast to the quantile value.
"""
red_imp_mat = sparse.csr_matrix(np.quantile(self.imp_mat.toarray(), q, axis=0))
red_at_event = np.array([red_imp_mat.sum()])
if event_name is None:
event_name = f"quantile_{q}"
return ImpactForecast(
frequency_unit=self.frequency_unit,
coord_exp=self.coord_exp,
crs=self.crs,
eai_exp=self.eai_exp,
at_event=red_at_event,
tot_value=self.tot_value,
aai_agg=self.aai_agg,
unit=self.unit,
imp_mat=red_imp_mat,
haz_type=self.haz_type,
**self._reduce_attrs(event_name),
)

def quantile(self, q: float):
"""
Reduce the impact matrix and at_event of an ImpactForecast to the quantile value.

Parameters
----------
q : float
The quantile to compute, which must be between 0 and 1.

Returns
-------
ImpactForecast
An ImpactForecast object with the quantile impact matrix and at_event.
"""
return self._quantile(q=q)

def median(self):
"""
Reduce the impact matrix and at_event of an ImpactForecast to the median value.

Parameters
----------
None

Returns
-------
ImpactForecast
An ImpactForecast object with the median impact matrix and at_event.
"""
return self._quantile(q=0.5, event_name="median")
46 changes: 46 additions & 0 deletions climada/engine/test/test_impact_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,49 @@ def test_impact_forecast_min_mean_max(impact_forecast_stats, attr):
npt.assert_array_equal(imp_fc_reduced.event_id, [0])
npt.assert_array_equal(imp_fc_reduced.frequency, [1])
npt.assert_array_equal(imp_fc_reduced.date, [0])


@pytest.mark.parametrize("quantile", [0.3, 0.6, 0.8])
def test_impact_forecast_quantile(impact_forecast, quantile):
"""Check quantile method for ImpactForecast"""
imp_fcst_quantile = impact_forecast.quantile(q=quantile)

# assert imp_mat
npt.assert_array_equal(
imp_fcst_quantile.imp_mat.toarray().squeeze(),
np.quantile(impact_forecast.imp_mat.toarray(), quantile, axis=0),
)
# assert at_event
npt.assert_array_equal(
imp_fcst_quantile.at_event,
np.quantile(impact_forecast.at_event, quantile, axis=0).sum(),
)

# check that attributes where reduced correctly
npt.assert_array_equal(imp_fcst_quantile.member, np.array([-1]))
npt.assert_array_equal(
imp_fcst_quantile.lead_time, np.array([np.timedelta64("NaT")])
)
npt.assert_array_equal(imp_fcst_quantile.event_id, np.array([0]))
npt.assert_array_equal(
imp_fcst_quantile.event_name, np.array([f"quantile_{quantile}"])
)
npt.assert_array_equal(imp_fcst_quantile.frequency, np.array([1]))
npt.assert_array_equal(imp_fcst_quantile.date, np.array([0]))


def test_median(impact_forecast):
imp_fcst_median = impact_forecast.median()
imp_fcst_quantile = impact_forecast.quantile(q=0.5)
npt.assert_array_equal(
imp_fcst_median.imp_mat.toarray(), imp_fcst_quantile.imp_mat.toarray()
)
npt.assert_array_equal(imp_fcst_median.imp_mat.toarray(), [[2.5, 2.5]])

# check that attributes where reduced correctly
npt.assert_array_equal(imp_fcst_median.member, np.array([-1]))
npt.assert_array_equal(imp_fcst_median.lead_time, np.array([np.timedelta64("NaT")]))
npt.assert_array_equal(imp_fcst_median.event_id, np.array([0]))
npt.assert_array_equal(imp_fcst_median.event_name, np.array(["median"]))
npt.assert_array_equal(imp_fcst_median.frequency, np.array([1]))
npt.assert_array_equal(imp_fcst_median.date, np.array([0]))
57 changes: 57 additions & 0 deletions climada/hazard/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,60 @@ def select(
extent=extent,
reset_frequency=reset_frequency,
)

def _quantile(self, q: float, event_name: str | None = None):
"""
Reduce the impact matrix and at_event of a HazardForecast to the quantile value.
"""
red_intensity = sparse.csr_matrix(
np.quantile(self.intensity.toarray(), q, axis=0)
)
red_fraction = sparse.csr_matrix(
np.quantile(self.fraction.toarray(), q, axis=0)
)
if event_name is None:
event_name = f"quantile_{q}"
return HazardForecast(
haz_type=self.haz_type,
pool=self.pool,
units=self.units,
centroids=self.centroids,
frequency_unit=self.frequency_unit,
intensity=red_intensity,
fraction=red_fraction,
**self._reduce_attrs(event_name),
)

def quantile(self, q: float):
"""
Reduce the impact matrix and at_event of a HazardForecast to the quantile value.

The quantile value is computed by taking the quantile of the impact matrix
along the event dimension axis (axis=0) and then taking the quantile of the
resulting array.

Parameters
----------
q : float
The quantile to compute, between 0 and 1.

Returns
-------
HazardForecast
A HazardForecast object with the quantile intensity and fraction.
"""
return self._quantile(q=q)

def median(self):
"""
Reduce the impact matrix and at_event of a HazardForecast to the median value.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Reduce the impact matrix and at_event of a HazardForecast to the median value.
Reduce the impact matrix and at_event of a HazardForecast to the median value over all events.


The median value is computed by taking the median of the impact matrix along the
event dimension axis (axis=0) and then taking the median of the resulting array.

Returns
-------
HazardForecast
A HazardForecast object with the median intensity and fraction.
"""
return self._quantile(q=0.5, event_name="median")
58 changes: 58 additions & 0 deletions climada/hazard/test/test_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,61 @@ def test_hazard_forecast_mean_min_max(haz_fc, attr):
npt.assert_array_equal(haz_fcst_reduced.frequency, [1])
npt.assert_array_equal(haz_fcst_reduced.date, [0])
npt.assert_array_equal(haz_fcst_reduced.orig, [True])


@pytest.mark.parametrize("quantile", [0.3, 0.6, 0.8])
def test_hazard_forecast_quantile(haz_fc, quantile):
"""Check quantile method for HazardForecast"""
haz_fcst_quantile = haz_fc.quantile(q=quantile)

# assert intensity
npt.assert_array_equal(
haz_fcst_quantile.intensity.toarray().squeeze(),
np.quantile(haz_fc.intensity.toarray(), quantile, axis=0),
)
# assert fraction
npt.assert_array_equal(
haz_fcst_quantile.fraction.toarray().squeeze(),
np.quantile(haz_fc.fraction.toarray(), quantile, axis=0),
)

# check that attributes where reduced correctly
npt.assert_array_equal(
haz_fcst_quantile.lead_time, np.array([np.timedelta64("NaT")])
)
npt.assert_array_equal(haz_fcst_quantile.member, np.array([-1]))
npt.assert_array_equal(
haz_fcst_quantile.event_name, np.array([f"quantile_{quantile}"])
)
npt.assert_array_equal(haz_fcst_quantile.event_id, np.array([0]))
npt.assert_array_equal(haz_fcst_quantile.frequency, np.array([1]))
npt.assert_array_equal(haz_fcst_quantile.date, np.array([0]))
npt.assert_array_equal(haz_fcst_quantile.orig, np.array([True]))


def test_median(haz_fc):
haz_fcst_median = haz_fc.median()
haz_fcst_quantile = haz_fc.quantile(q=0.5)
npt.assert_array_equal(
haz_fcst_median.intensity.todense(), haz_fcst_quantile.intensity.todense()
)
npt.assert_array_equal(
haz_fcst_median.intensity.todense(),
np.median(haz_fc.intensity.todense(), axis=0),
)
npt.assert_array_equal(
haz_fcst_median.fraction.todense(), haz_fcst_quantile.fraction.todense()
)
npt.assert_array_equal(
haz_fcst_median.fraction.todense(),
np.median(haz_fc.fraction.todense(), axis=0),
)

# check that attributes where reduced correctly
npt.assert_array_equal(haz_fcst_median.member, np.array([-1]))
npt.assert_array_equal(haz_fcst_median.lead_time, np.array([np.timedelta64("NaT")]))
npt.assert_array_equal(haz_fcst_median.event_id, np.array([0]))
npt.assert_array_equal(haz_fcst_median.event_name, np.array(["median"]))
npt.assert_array_equal(haz_fcst_median.frequency, np.array([1]))
npt.assert_array_equal(haz_fcst_median.date, np.array([0]))
npt.assert_array_equal(haz_fcst_median.orig, np.array([True]))
Loading