Skip to content

Commit 2ace55f

Browse files
timschmi95Schmid  Timopeanutfun
authored
Add function to plot Impf variability of calibration (#791)
Co-authored-by: Schmid Timo <[email protected]> Co-authored-by: Lukas Riedel <[email protected]>
1 parent 2e536ef commit 2ace55f

File tree

2 files changed

+239
-81
lines changed

2 files changed

+239
-81
lines changed

climada/util/calibrate/base.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
import pandas as pd
99
import numpy as np
1010
from scipy.optimize import Bounds, LinearConstraint, NonlinearConstraint
11+
import matplotlib.pyplot as plt
1112
import seaborn as sns
1213

1314
from climada.hazard import Hazard
1415
from climada.entity import Exposures, ImpactFuncSet
1516
from climada.engine import Impact, ImpactCalc
1617
import climada.util.coordinates as u_coord
18+
# from .bayesian_optimizer import BayesianOptimizerOutput
1719

1820
ConstraintType = Union[LinearConstraint, NonlinearConstraint, Mapping]
1921

@@ -158,6 +160,131 @@ def plot_impf_set(self, **plot_kwargs):
158160
"""
159161
return self.impf_set.plot(**plot_kwargs)
160162

163+
def plot_impf_variability(
164+
self,
165+
cost_func_diff: float = 0.1,
166+
p_space_df: Optional[pd.DataFrame] = None,
167+
plot_haz: bool = True,
168+
plot_impf_kws: Optional[dict] = None,
169+
plot_hist_kws: Optional[dict] = None,
170+
):
171+
172+
"""Plot impact function variability with parameter combinations of
173+
almost equal cost function values
174+
175+
Args:
176+
cost_func_diff (float, optional): Max deviation from optimal cost
177+
function value (as fraction). Defaults to 0.1 (i.e. 10%).
178+
p_space_df (pd.DataFrame, optional): parameter space. Defaults to None.
179+
plot_haz (bool, optional): Whether or not to plot hazard intensity
180+
distibution. Defaults to False.
181+
plot_impf_kws (dict, optional): Keyword arguments for impact
182+
function plot. Defaults to None.
183+
plot_hist_kws (dict, optional): Keyword arguments for hazard
184+
intensity distribution plot. Defaults to None.
185+
"""
186+
187+
# Initialize plot keyword arguments
188+
if plot_impf_kws is None:
189+
plot_impf_kws = {}
190+
if plot_hist_kws is None:
191+
plot_hist_kws = {}
192+
193+
# Retrieve hazard type and parameter space
194+
haz_type = self.input.hazard.haz_type
195+
if p_space_df is None:
196+
# Assert that self.output has the p_space_to_dataframe() method,
197+
# which is defined for the BayesianOptimizerOutput class
198+
if not hasattr(self.output,"p_space_to_dataframe"):
199+
raise TypeError(
200+
"To derive the full impact function parameter space, "
201+
"plot_impf_variability() requires BayesianOptimizerOutput "
202+
"as OutputEvaluator.output attribute, which provides the "
203+
"method p_space_to_dataframe()."
204+
)
205+
p_space_df = self.output.p_space_to_dataframe()
206+
207+
# Retrieve list of parameters required for creating impact functions
208+
# and remove the dimension 'Cost Function'.
209+
params = p_space_df.columns.tolist()
210+
try:
211+
params.remove('Cost Function')
212+
except ValueError:
213+
pass
214+
215+
# Retrieve parameters of impact functions with cost function values
216+
# within 'cost_func_diff' % of the best estimate
217+
params_within_range = p_space_df[params]
218+
plot_space_label = 'Parameter space'
219+
if cost_func_diff is not None:
220+
max_cost_func_val = (p_space_df['Cost Function'].min()*
221+
(1+cost_func_diff))
222+
params_within_range = p_space_df.loc[
223+
p_space_df['Cost Function'] <=max_cost_func_val,params
224+
]
225+
plot_space_label = (f"within {int(cost_func_diff*100)} percent "
226+
f"of best fit")
227+
228+
# Set plot defaults
229+
color = plot_impf_kws.pop('color','tab:blue')
230+
lw = plot_impf_kws.pop('lw',2)
231+
zorder = plot_impf_kws.pop('zorder',3)
232+
label = plot_impf_kws.pop('label','best fit')
233+
234+
#get number of impact functions and create a plot for each
235+
n_impf = len(self.impf_set.get_func(haz_type=haz_type))
236+
axes=[]
237+
238+
for impf_idx in range(n_impf):
239+
240+
_,ax = plt.subplots()
241+
242+
#Plot best-fit impact function
243+
best_impf = self.impf_set.get_func(haz_type=haz_type)[impf_idx]
244+
ax.plot(best_impf.intensity,best_impf.mdd*best_impf.paa*100,
245+
color=color,lw=lw,zorder=zorder,label=label,**plot_impf_kws)
246+
247+
#Plot all impact functions within 'cost_func_diff' % of best estimate
248+
for row in range(params_within_range.shape[0]):
249+
label_temp = plot_space_label if row == 0 else None
250+
251+
sel_params = params_within_range.iloc[row,:].to_dict()
252+
temp_impf_set = self.input.impact_func_creator(**sel_params)
253+
temp_impf = temp_impf_set.get_func(haz_type=haz_type)[impf_idx]
254+
255+
ax.plot(temp_impf.intensity,temp_impf.mdd*temp_impf.paa*100,
256+
color='grey',alpha=0.4,label=label_temp)
257+
258+
# Plot hazard intensity value distributions
259+
if plot_haz:
260+
haz_vals = self.input.hazard.intensity[
261+
:, self.input.exposure.gdf[f"centr_{haz_type}"]
262+
]
263+
264+
#Plot defaults
265+
color_hist = plot_hist_kws.pop('color','tab:orange')
266+
alpha_hist = plot_hist_kws.pop('alpha',0.3)
267+
268+
ax2 = ax.twinx()
269+
ax2.hist(haz_vals.data,bins=40,color=color_hist,
270+
alpha=alpha_hist,label='Hazard intensity\noccurence')
271+
ax2.set(ylabel='Hazard intensity occurence (#Exposure points)')
272+
ax.axvline(x=haz_vals.max(),label='Maximum hazard value',
273+
color='tab:orange')
274+
ax2.legend(loc='lower right')
275+
276+
ax.set(xlabel=f"Intensity ({self.input.hazard.units})",
277+
ylabel="Mean Damage Ratio (MDR) in %",
278+
xlim=(min(best_impf.intensity),max(best_impf.intensity)))
279+
ax.legend()
280+
axes.append(ax)
281+
282+
if n_impf > 1:
283+
return axes
284+
285+
return ax
286+
287+
161288
def plot_at_event(
162289
self,
163290
data_transf: Callable[[pd.DataFrame], pd.DataFrame] = lambda x: x,

0 commit comments

Comments
 (0)