Skip to content

Commit e44b9f0

Browse files
refactorize methods by introducing new interpolate wrapper function and new util function
1 parent d658bbc commit e44b9f0

File tree

6 files changed

+187
-140
lines changed

6 files changed

+187
-140
lines changed

climada/engine/impact.py

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import climada.util.plot as u_plot
5454
from climada.util.select import get_attributes_with_matching_dimension
5555
import climada.util.interpolation as u_interp
56+
import climada.util.checker as u_check
5657

5758
LOGGER = logging.getLogger(__name__)
5859

@@ -523,55 +524,32 @@ def local_exceedance_impact(
523524
if self.imp_mat.size == 0:
524525
raise ValueError('Attribute imp_mat is empty. Recalculate Impact'
525526
'instance with parameter save_mat=True')
527+
526528
#check frequency unit
527-
if self.frequency_unit in ['1/year', 'annual', '1/y', '1/a']:
528-
return_period_unit = 'years'
529-
elif self.frequency_unit in ['1/month', 'monthly', '1/m']:
530-
return_period_unit = 'months'
531-
elif self.frequency_unit in ['1/week', 'weekly', '1/w']:
532-
return_period_unit = 'weeks'
533-
else:
534-
LOGGER.warning("Hazard's frequency unit %s is not known, "
535-
"years will be used as return period unit.",
536-
self.frequency_unit)
537-
return_period_unit = 'years'
538-
539-
num_centroids = self.imp_mat.shape[1]
540-
imp_stats = np.zeros((len(return_periods), num_centroids))
541-
542-
for i in range(num_centroids):
543-
# sort intensties and frequencies at given centroid
544-
impact = np.squeeze(self.imp_mat[:,i].toarray())
545-
sorted_idxs = np.argsort(impact)
546-
impact = np.squeeze(impact[sorted_idxs])
547-
frequency = self.frequency[sorted_idxs]
548-
549-
# group values with same impact
550-
frequency, impact = u_interp.group_frequency(frequency, impact)
551-
552-
# fit intensities to cummulative frequencies
553-
frequency = np.cumsum(frequency[::-1])[::-1]
554-
if method == 'stepfunction':
555-
imp_stats[:,i] = u_interp.stepfunction_ev(
556-
1/np.array(return_periods), frequency[::-1], impact[::-1],
557-
y_threshold=min_impact, y_asymptotic=0.
558-
)
559-
elif method in ['interpolate', 'extrapolate', 'extrapolate_constant']:
560-
extrapolation = None if method == 'interpolate' else method
561-
imp_stats[:,i] = u_interp.interpolate_ev(
562-
1/np.array(return_periods), frequency[::-1], impact[::-1],
563-
logx=log_frequency, logy=log_impact, y_threshold=min_impact,
564-
extrapolation=extrapolation, y_asymptotic=0.
565-
)
566-
else:
567-
raise ValueError(f"Unknown method: {method}")
529+
return_period_unit = u_check.convert_frequency_unit_to_time_unit(self.frequency_unit)
530+
531+
# check method
532+
if method not in ['interpolate', 'extrapolate', 'extrapolate_constant', 'stepfunction']:
533+
raise ValueError(f"Unknown method: {method}")
534+
535+
# calculate local exceedance impact
536+
test_frequency = 1/np.array(return_periods)
537+
exceedance_impact = np.array([
538+
u_interp.preprocess_and_interpolate_ev(
539+
test_frequency, None, self.frequency,
540+
self.imp_mat.getcol(i_centroid).toarray().flatten(),
541+
log_frequency=log_frequency, log_values=log_impact,
542+
value_threshold=min_impact, method=method, y_asymptotic=0.
543+
)
544+
for i_centroid in range(self.imp_mat.shape[1])
545+
])
568546

569547
# create the output GeoDataFrame
570548
gdf = gpd.GeoDataFrame(
571549
geometry = gpd.points_from_xy(self.coord_exp[:,1], self.coord_exp[:,0]),
572550
crs = self.crs)
573551
col_names = [f'{ret_per}' for ret_per in return_periods]
574-
gdf[col_names] = imp_stats.T
552+
gdf[col_names] = exceedance_impact
575553
# create label and column_label
576554
label = f'Impact ({self.unit})'
577555
column_label = lambda column_names: [f'Return Period: {col} {return_period_unit}'

climada/hazard/base.py

Lines changed: 37 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ def local_exceedance_intensity(
453453
return_periods=(25, 50, 100, 250),
454454
method='interpolate',
455455
min_intensity=None,
456-
log_frequeny=True,
456+
log_frequency=True,
457457
log_intensity=True
458458
):
459459
"""Compute local exceedance intensity for given return periods. The default method
@@ -502,51 +502,28 @@ def local_exceedance_intensity(
502502
if not min_intensity and min_intensity != 0:
503503
min_intensity = self.intensity_thres
504504
#check frequency unit
505-
if self.frequency_unit in ['1/year', 'annual', '1/y', '1/a']:
506-
return_period_unit = 'years'
507-
elif self.frequency_unit in ['1/month', 'monthly', '1/m']:
508-
return_period_unit = 'months'
509-
elif self.frequency_unit in ['1/week', 'weekly', '1/w']:
510-
return_period_unit = 'weeks'
511-
else:
512-
LOGGER.warning(f"Hazard's frequency unit {self.frequency_unit} is not known, "
513-
"years will be used as return period unit.")
514-
return_period_unit = 'years'
515-
516-
num_centroids = self.intensity.shape[1]
517-
inten_stats = np.zeros((len(return_periods), num_centroids))
518-
519-
for i in range(num_centroids):
520-
# sort intensties and frequencies at given centroid
521-
intensity = np.squeeze(self.intensity[:,i].toarray())
522-
sorted_idxs = np.argsort(intensity)
523-
intensity = np.squeeze(intensity[sorted_idxs])
524-
frequency = self.frequency[sorted_idxs]
525-
526-
# group values with same intensity
527-
frequency, intensity = u_interp.group_frequency(frequency, intensity)
528-
529-
# fit intensities to cummulative frequencies
530-
frequency = np.cumsum(frequency[::-1])[::-1]
531-
if method == 'stepfunction':
532-
inten_stats[:,i] = u_interp.stepfunction_ev(
533-
1/np.array(return_periods), frequency[::-1], intensity[::-1],
534-
y_threshold=min_intensity, y_asymptotic=0.
535-
)
536-
elif method in ['interpolate', 'extrapolate', 'extrapolate_constant']:
537-
extrapolation = None if method == 'interpolate' else method
538-
inten_stats[:,i] = u_interp.interpolate_ev(
539-
1/np.array(return_periods), frequency[::-1], intensity[::-1],
540-
logx=log_frequeny, logy=log_intensity, y_threshold=min_intensity,
541-
extrapolation=extrapolation, y_asymptotic=0.
542-
)
543-
else:
544-
raise ValueError(f"Unknown method: {method}")
505+
return_period_unit = u_check.convert_frequency_unit_to_time_unit(self.frequency_unit)
506+
507+
# check method
508+
if method not in ['interpolate', 'extrapolate', 'extrapolate_constant', 'stepfunction']:
509+
raise ValueError(f"Unknown method: {method}")
510+
511+
# calculate local exceedance intensity
512+
test_frequency = 1/np.array(return_periods)
513+
exceedance_intensity = np.array([
514+
u_interp.preprocess_and_interpolate_ev(
515+
test_frequency, None, self.frequency,
516+
self.intensity.getcol(i_centroid).toarray().flatten(),
517+
log_frequency=log_frequency, log_values=log_intensity,
518+
value_threshold=min_intensity, method=method, y_asymptotic=0.
519+
)
520+
for i_centroid in range(self.intensity.shape[1])
521+
])
545522

546523
# create the output GeoDataFrame
547524
gdf = gpd.GeoDataFrame(geometry = self.centroids.gdf['geometry'], crs = self.centroids.gdf.crs)
548525
column_names = [f'{rp}' for rp in return_periods]
549-
gdf[column_names] = inten_stats.T
526+
gdf[column_names] = exceedance_intensity
550527

551528
# create label and column_label
552529
label = f'Intensity ({self.units})'
@@ -624,55 +601,29 @@ def local_return_period(
624601
if not min_intensity and min_intensity != 0:
625602
min_intensity = self.intensity_thres
626603
#check frequency unit
627-
if self.frequency_unit in ['1/year', 'annual', '1/y', '1/a']:
628-
return_period_unit = 'Years'
629-
elif self.frequency_unit in ['1/month', 'monthly', '1/m']:
630-
return_period_unit = 'Months'
631-
elif self.frequency_unit in ['1/week', 'weekly', '1/w']:
632-
return_period_unit = 'Weeks'
633-
else:
634-
LOGGER.warning("Hazard's frequency unit %s is not known, "
635-
"years will be used as return period unit.", self.frequency_unit)
636-
return_period_unit = 'Years'
637-
638-
# Ensure threshold_intensities is a numpy array
639-
threshold_intensities = np.array(threshold_intensities)
640-
641-
num_centroids = self.intensity.shape[1]
642-
return_periods = np.zeros((len(threshold_intensities), num_centroids))
643-
644-
for i in range(num_centroids):
645-
# sort intensties and frequencies at given centroid
646-
intensity = np.squeeze(self.intensity[:,i].toarray())
647-
sorted_idxs = np.argsort(intensity)
648-
intensity = np.squeeze(intensity[sorted_idxs])
649-
frequency = self.frequency[sorted_idxs]
650-
651-
# group values with same intensity
652-
frequency, intensity = u_interp.group_frequency(frequency, intensity)
653-
654-
# fit intensities to cummulative frequencies
655-
frequency = np.cumsum(frequency[::-1])[::-1]
656-
if method == 'stepfunction':
657-
return_periods[:,i] = u_interp.stepfunction_ev(
658-
threshold_intensities, intensity, frequency, x_threshold=min_intensity
659-
)
660-
elif method in ['interpolate', 'extrapolate', 'extrapolate_constant']:
661-
extrapolation = None if method == 'interpolate' else method
662-
return_periods[:,i] = u_interp.interpolate_ev(
663-
threshold_intensities, intensity, frequency, logx=log_intensity,
664-
logy=log_frequency, x_threshold=min_intensity, extrapolation=extrapolation,
665-
y_asymptotic=np.nan
666-
)
667-
else:
668-
raise ValueError(f"Unknown method: {method}")
604+
return_period_unit = u_check.convert_frequency_unit_to_time_unit(self.frequency_unit)
605+
606+
# check method
607+
if method not in ['interpolate', 'extrapolate', 'extrapolate_constant', 'stepfunction']:
608+
raise ValueError(f"Unknown method: {method}")
609+
610+
# calculate local return periods
611+
return_periods = np.array([
612+
u_interp.preprocess_and_interpolate_ev(
613+
None, np.array(threshold_intensities), self.frequency,
614+
self.intensity.getcol(i_centroid).toarray().flatten(),
615+
log_frequency=log_frequency, log_values=log_intensity,
616+
value_threshold=min_intensity, method=method, y_asymptotic=np.nan
617+
)
618+
for i_centroid in range(self.intensity.shape[1])
619+
])
669620
return_periods = safe_divide(1., return_periods)
670-
621+
671622
# create the output GeoDataFrame
672623
gdf = gpd.GeoDataFrame(geometry = self.centroids.gdf['geometry'],
673624
crs = self.centroids.gdf.crs)
674625
col_names = [f'{tresh_inten}' for tresh_inten in threshold_intensities]
675-
gdf[col_names] = return_periods.T
626+
gdf[col_names] = return_periods
676627

677628
# create label and column_label
678629
label = f'Return Periods ({return_period_unit})'

climada/hazard/test/test_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ def test_local_exceedance_intensity(self):
10491049
# third centroid has intensities 1 with cum frequencies 1
10501050
# testing at frequencies 2, 1.5, 1
10511051
inten_stats, _, _ = haz.local_exceedance_intensity(
1052-
return_period, log_frequeny=False, log_intensity=False, method='extrapolate_constant')
1052+
return_period, log_frequency=False, log_intensity=False, method='extrapolate_constant')
10531053
np.testing.assert_allclose(
10541054
inten_stats[inten_stats.columns[1:]].values,
10551055
np.array([

climada/test/test_hazard.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ def test_local_exceedance_intensity_methods(self):
458458

459459
# test lin lin interpolation without extrapolation
460460
inten_stats, _, _ = haz.local_exceedance_intensity(
461-
return_periods=(1000, 30, .1), log_frequeny=False, log_intensity=False,
461+
return_periods=(1000, 30, .1), log_frequency=False, log_intensity=False,
462462
method='extrapolate_constant')
463463
np.testing.assert_allclose(
464464
inten_stats.values[:,1:].astype(float),

climada/util/checker.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,29 @@ def prune_csr_matrix(matrix: sparse.csr_matrix):
214214
matrix.check_format()
215215
matrix.eliminate_zeros()
216216
matrix.sum_duplicates()
217+
218+
def convert_frequency_unit_to_time_unit(frequency_unit):
219+
"""Converts common frequency units to corresponding time units. Unknown frequency
220+
units are converted to "years".
221+
222+
Parameters
223+
----------
224+
frequency_unit : str
225+
unit of frequency
226+
227+
Returns
228+
-------
229+
str
230+
corresponding time unit.
231+
"""
232+
if frequency_unit in ['1/year', 'annual', '1/y', '1/a']:
233+
time_unit = 'years'
234+
elif frequency_unit in ['1/month', 'monthly', '1/m']:
235+
time_unit = 'months'
236+
elif frequency_unit in ['1/week', 'weekly', '1/w']:
237+
time_unit = 'weeks'
238+
else:
239+
LOGGER.warning(f"Frequency unit {frequency_unit} is not known, "
240+
"years will be used as time unit.")
241+
time_unit = 'years'
242+
return time_unit

0 commit comments

Comments
 (0)