@@ -488,6 +488,7 @@ def local_exceedance_intensity(
488488 min_intensity = None ,
489489 log_frequency = True ,
490490 log_intensity = True ,
491+ bin_decimals = None ,
491492 ):
492493 """Compute local exceedance intensity for given return periods. The default method
493494 is fitting the ordered intensitites per centroid to the corresponding cummulated
@@ -506,19 +507,23 @@ def local_exceedance_intensity(
506507 periods larger than the Hazard object's observed local return periods will be assigned
507508 the largest local intensity, and return periods smaller than the Hazard object's
508509 observed local return periods will be assigned 0. If set to "extrapolate", local
509- exceedance intensities will be extrapolated (and interpolated).
510- Defauls to "interpolate".
510+ exceedance intensities will be extrapolated (and interpolated). The extrapolation to
511+ large return periods uses the two highest intensites of the centroid and their return
512+ periods and extends the interpolation between these points to the given return period
513+ (similar for small return periods). Defauls to "interpolate".
511514 min_intensity : float, optional
512515 Minimum threshold to filter the hazard intensity. If set to None, self.intensity_thres
513516 will be used. Defaults to None.
514517 log_frequency : bool, optional
515- This parameter is only used if method is set to "interpolate". If set to True,
516- (cummulative) frequency values are converted to log scale before inter- and
517- extrapolation. Defaults to True.
518+ If set to True, (cummulative) frequency values are converted to log scale before
519+ inter- and extrapolation. Defaults to True.
518520 log_intensity : bool, optional
519- This parameter is only used if method is set to "interpolate". If set to True,
520- intensity values are converted to log scale before inter- and extrapolation.
521- Defaults to True.
521+ If set to True, intensity values are converted to log scale before
522+ inter- and extrapolation. Defaults to True.
523+ bin_decimals : int, optional
524+ Number of decimals to group and bin intensity values. Binning results in smoother (and
525+ coarser) interpolation and more stable extrapolation. For more details and sensible
526+ values for bin_decimals, see Notes. If None, values are not binned. Defaults to None.
522527
523528 Returns
524529 -------
@@ -531,6 +536,23 @@ def local_exceedance_intensity(
531536 GeoDataFrame label, for reporting and plotting
532537 column_label : function
533538 Column-label-generating function, for reporting and plotting
539+
540+ See Also
541+ --------
542+ util.interpolation.preprocess_and_interpolate_ev :
543+ inter- and extrapolation method
544+
545+ Notes
546+ -------
547+ If an integer bin_decimals is given, the intensity values are binned according to their
548+ bin_decimals decimals, and their corresponding frequencies are summed. This binning leads
549+ to a smoother (and coarser) interpolation, and a more stable extrapolation. For instance,
550+ if bin_decimals=1, the two values 12.01 and 11.97 with corresponding frequencies 0.1 and
551+ 0.2 are combined to a value 12.0 with frequency 0.3. The default bin_decimals=None results
552+ in not binning the values.
553+ E.g., if your intensities range from 1 to 100, you could use bin_decimals=1, if your
554+ intensities range from 1e6 to 1e9, you could use bin_decimals=-5, if your intensities
555+ range from 0.0001 to .01, you could use bin_decimals=5.
534556 """
535557 if not min_intensity and min_intensity != 0 :
536558 min_intensity = self .intensity_thres
@@ -550,23 +572,32 @@ def local_exceedance_intensity(
550572
551573 # calculate local exceedance intensity
552574 test_frequency = 1 / np .array (return_periods )
553- exceedance_intensity = np .array (
554- [
555- u_interp .preprocess_and_interpolate_ev (
556- test_frequency ,
557- None ,
558- self .frequency ,
559- self .intensity .getcol (i_centroid ).toarray ().flatten (),
560- log_frequency = log_frequency ,
561- log_values = log_intensity ,
562- value_threshold = min_intensity ,
563- method = method ,
564- y_asymptotic = 0.0 ,
565- )
566- for i_centroid in range (self .intensity .shape [1 ])
567- ]
575+
576+ exceedance_intensity = np .full (
577+ (self .intensity .shape [1 ], len (test_frequency )),
578+ np .nan if method == "interpolate" else 0.0 ,
568579 )
569580
581+ nonzero_centroids = np .where (self .intensity .getnnz (axis = 0 ) > 0 )[0 ]
582+ if not len (nonzero_centroids ) == 0 :
583+ exceedance_intensity [nonzero_centroids , :] = np .array (
584+ [
585+ u_interp .preprocess_and_interpolate_ev (
586+ test_frequency ,
587+ None ,
588+ self .frequency ,
589+ self .intensity .getcol (i_centroid ).toarray ().flatten (),
590+ log_frequency = log_frequency ,
591+ log_values = log_intensity ,
592+ value_threshold = min_intensity ,
593+ method = method ,
594+ y_asymptotic = 0.0 ,
595+ bin_decimals = bin_decimals ,
596+ )
597+ for i_centroid in nonzero_centroids
598+ ]
599+ )
600+
570601 # create the output GeoDataFrame
571602 gdf = gpd .GeoDataFrame (
572603 geometry = self .centroids .gdf ["geometry" ], crs = self .centroids .gdf .crs
@@ -576,9 +607,11 @@ def local_exceedance_intensity(
576607
577608 # create label and column_label
578609 label = f"Intensity ({ self .units } )"
579- column_label = lambda column_names : [
580- f"Return Period: { col } { return_period_unit } " for col in column_names
581- ]
610+
611+ def column_label (column_names ):
612+ return [
613+ f"Return Period: { col } { return_period_unit } " for col in column_names
614+ ]
582615
583616 return gdf , label , column_label
584617
@@ -609,6 +642,7 @@ def local_return_period(
609642 min_intensity = None ,
610643 log_frequency = True ,
611644 log_intensity = True ,
645+ bin_decimals = None ,
612646 ):
613647 """Compute local return periods for given hazard intensities. The default method
614648 is fitting the ordered intensitites per centroid to the corresponding cummulated
@@ -628,18 +662,24 @@ def local_return_period(
628662 intensities will be assigned NaN, and threshold intensities smaller than the Hazard
629663 object's local intensities will be assigned the smallest observed local return period.
630664 If set to "extrapolate", local return periods will be extrapolated (and interpolated).
665+ The extrapolation to large threshold intensities uses the two highest intensites of
666+ the centroid and their return periods and extends the interpolation between these
667+ points to the given threshold intensity (similar for small threshold intensites).
631668 Defaults to "interpolate".
632669 min_intensity : float, optional
633670 Minimum threshold to filter the hazard intensity. If set to None, self.intensity_thres
634671 will be used. Defaults to None.
635672 log_frequency : bool, optional
636- This parameter is only used if method is set to "interpolate". If set to True,
637- (cummulative) frequency values are converted to log scale before inter- and
638- extrapolation. Defaults to True.
673+ If set to True, (cummulative) frequency values are converted to log scale before
674+ inter- and extrapolation. Defaults to True.
639675 log_intensity : bool, optional
640- This parameter is only used if method is set to "interpolate". If set to True,
641- intensity values are converted to log scale before inter- and extrapolation.
642- Defaults to True.
676+ If set to True, intensity values are converted to log scale before
677+ inter- and extrapolation. Defaults to True.
678+ bin_decimals : int, optional
679+ Number of decimals to group and bin intensity values. Binning results in smoother (and
680+ coarser) interpolation and more stable extrapolation. For more details and sensible
681+ values for bin_decimals, see Notes. If None, values are not binned. Defaults to None.
682+
643683
644684 Returns
645685 -------
@@ -652,6 +692,23 @@ def local_return_period(
652692 GeoDataFrame label, for reporting and plotting
653693 column_label : function
654694 Column-label-generating function, for reporting and plotting
695+
696+ See Also
697+ --------
698+ util.interpolation.preprocess_and_interpolate_ev :
699+ inter- and extrapolation method
700+
701+ Notes
702+ -------
703+ If an integer bin_decimals is given, the intensity values are binned according to their
704+ bin_decimals decimals, and their corresponding frequencies are summed. This binning leads
705+ to a smoother (and coarser) interpolation, and a more stable extrapolation. For instance,
706+ if bin_decimals=1, the two values 12.01 and 11.97 with corresponding frequencies 0.1 and
707+ 0.2 are combined to a value 12.0 with frequency 0.3. The default bin_decimals=None results
708+ in not binning the values.
709+ E.g., if your intensities range from 1 to 100, you could use bin_decimals=1, if your
710+ intensities range from 1e6 to 1e9, you could use bin_decimals=-5, if your intensities
711+ range from 0.0001 to .01, you could use bin_decimals=5.
655712 """
656713 if not min_intensity and min_intensity != 0 :
657714 min_intensity = self .intensity_thres
@@ -669,23 +726,31 @@ def local_return_period(
669726 ]:
670727 raise ValueError (f"Unknown method: { method } " )
671728
672- # calculate local return periods
673- return_periods = np .array (
674- [
675- u_interp .preprocess_and_interpolate_ev (
676- None ,
677- np .array (threshold_intensities ),
678- self .frequency ,
679- self .intensity .getcol (i_centroid ).toarray ().flatten (),
680- log_frequency = log_frequency ,
681- log_values = log_intensity ,
682- value_threshold = min_intensity ,
683- method = method ,
684- y_asymptotic = np .nan ,
685- )
686- for i_centroid in range (self .intensity .shape [1 ])
687- ]
729+ return_periods = np .full (
730+ (self .intensity .shape [1 ], len (threshold_intensities )), np .nan
688731 )
732+
733+ nonzero_centroids = np .where (self .intensity .getnnz (axis = 0 ) > 0 )[0 ]
734+
735+ if not len (nonzero_centroids ) == 0 :
736+ return_periods [nonzero_centroids , :] = np .array (
737+ [
738+ u_interp .preprocess_and_interpolate_ev (
739+ None ,
740+ np .array (threshold_intensities ),
741+ self .frequency ,
742+ self .intensity .getcol (i_centroid ).toarray ().flatten (),
743+ log_frequency = log_frequency ,
744+ log_values = log_intensity ,
745+ value_threshold = min_intensity ,
746+ method = method ,
747+ y_asymptotic = np .nan ,
748+ bin_decimals = bin_decimals ,
749+ )
750+ for i_centroid in nonzero_centroids
751+ ]
752+ )
753+
689754 return_periods = safe_divide (1.0 , return_periods )
690755
691756 # create the output GeoDataFrame
@@ -697,9 +762,9 @@ def local_return_period(
697762
698763 # create label and column_label
699764 label = f"Return Periods ({ return_period_unit } )"
700- column_label = lambda column_names : [
701- f"Threshold Intensity: { col } { self . units } " for col in column_names
702- ]
765+
766+ def column_label ( column_names ):
767+ return [ f"Threshold Intensity: { col } { self . units } " for col in column_names ]
703768
704769 return gdf , label , column_label
705770
0 commit comments