From 489a0a94d38f196491c37b41b4f4b30ae537f40f Mon Sep 17 00:00:00 2001 From: Le Roux Erwan <erwan.le-roux@irstea.fr> Date: Fri, 28 Feb 2020 17:58:35 +0100 Subject: [PATCH] [paper 1] add extension of intensity plot with gev params. add inverse gumbel transformation. --- .../abstract_gev_trend_test.py | 92 +++++++++++-------- extreme_fit/distribution/gev/gev_params.py | 11 +++ .../qqplot/main_qqplot_for_big_shapes.py | 27 ++++-- ...dy_visualizer_for_non_stationary_trends.py | 2 +- 4 files changed, 87 insertions(+), 45 deletions(-) diff --git a/experiment/trend_analysis/univariate_test/extreme_trend_test/abstract_gev_trend_test.py b/experiment/trend_analysis/univariate_test/extreme_trend_test/abstract_gev_trend_test.py index 792dcb8e..6111998b 100644 --- a/experiment/trend_analysis/univariate_test/extreme_trend_test/abstract_gev_trend_test.py +++ b/experiment/trend_analysis/univariate_test/extreme_trend_test/abstract_gev_trend_test.py @@ -228,7 +228,7 @@ class AbstractGevTrendTest(AbstractUnivariateTest): print(unconstrained_empirical_quantiles) ax_twiny = ax.twiny() - return_periods = [10, 25, 50, 100] + return_periods = [10, 25, 50] quantiles = self.get_standard_quantiles_for_return_periods(return_periods, psnow) print(quantiles) ax_twiny.plot(quantiles, [0 for _ in quantiles], linewidth=0) @@ -237,42 +237,28 @@ class AbstractGevTrendTest(AbstractUnivariateTest): ax_twiny.set_xlabel('Return period w.r.t all annual maxima of GSL (years)', fontsize=size) # Plot for the discarded model - if 'Verdon' in massif_name and altitude == 300: - q = [-1.4541688117485054, -1.2811308174310914, -1.216589300814509, -0.7635793791201918, -0.6298883422064275, - -0.5275954855697504, -0.4577268043676126, -0.4497570331795861, -0.1647955002136654, - -0.14492222503785876, -0.139173823298689, -0.11945617994263039, -0.07303100174657867, - -5.497308509286266e-05, 0.13906416388625908, 0.15274793441408543, 0.1717763342727519, - 0.17712605315013535, 0.17900143646245203, 0.371986176207554, 0.51640780422156, 0.7380550963951035, - 0.7783015252180445, 0.887836077295502, 0.917853338231094, 0.9832396811506262, 1.0359396416309927, - 1.1892663813729711, 1.2053261113817888, 1.5695111391491652, 2.3223652143938476, 2.674882764437432, - 2.6955728524900406, 2.8155882785356896, 3.282838470153471, 3.2885313947906765] - color = 'red' - ax.plot(q, sorted_maxima, - label='Discarded model, which is ${}$\n'.format('\mathcal{M}_{\zeta_0, \sigma_1}') - + 'with $\zeta_0=0.84$', - color=color) - - # Extend the curve linear a bit if the return period 50 is not in the quantiles - - def compute_slope_intercept(x, y): - x1, x2 = x[-2:] - y1, y2 = y[-2:] - a = (y2 - y1) / (x2 - x1) - b = y1 - a * x1 - return a, b - - def compute_maxima_corresponding_to_return_period(return_period_quantiles, quantiles, model_maxima): - a, b = compute_slope_intercept(quantiles, model_maxima) - return a * return_period_quantiles + b - - quantile_return_period_50 = quantiles[-1] - if max(q) < quantile_return_period_50: - maxima_extended = compute_maxima_corresponding_to_return_period(quantile_return_period_50, - q, - sorted_maxima) - ax.plot([q[-1], quantile_return_period_50], - [sorted_maxima[-1], maxima_extended], linestyle='--', - color=color) + # if 'Verdon' in massif_name and altitude == 300: + # q = [-1.4541688117485054, -1.2811308174310914, -1.216589300814509, -0.7635793791201918, -0.6298883422064275, + # -0.5275954855697504, -0.4577268043676126, -0.4497570331795861, -0.1647955002136654, + # -0.14492222503785876, -0.139173823298689, -0.11945617994263039, -0.07303100174657867, + # -5.497308509286266e-05, 0.13906416388625908, 0.15274793441408543, 0.1717763342727519, + # 0.17712605315013535, 0.17900143646245203, 0.371986176207554, 0.51640780422156, 0.7380550963951035, + # 0.7783015252180445, 0.887836077295502, 0.917853338231094, 0.9832396811506262, 1.0359396416309927, + # 1.1892663813729711, 1.2053261113817888, 1.5695111391491652, 2.3223652143938476, 2.674882764437432, + # 2.6955728524900406, 2.8155882785356896, 3.282838470153471, 3.2885313947906765] + # color = 'red' + # ax.plot(q, sorted_maxima, + # label='Discarded model, which is ${}$\n'.format('\mathcal{M}_{\zeta_0, \sigma_1}') + # + 'with $\zeta_0=0.84$', + # color=color) + + if self.unconstrained_estimator_gev_params.shape > 0.5: + # self.linear_extension(ax, unconstrained_empirical_quantiles, quantiles, sorted_maxima) + max_model_quantile = max(unconstrained_empirical_quantiles) + # print(sorted(unconstrained_empirical_quantiles)) + # self.gev_param_extension(ax, 1958, max_model_quantile) + self.gev_param_extension(ax, 2017, max_model_quantile) + all_quantiles = standard_gumbel_quantiles + unconstrained_empirical_quantiles + quantiles epsilon = 0.5 @@ -281,11 +267,41 @@ class AbstractGevTrendTest(AbstractUnivariateTest): ax_twiny.set_xlim(ax_lim) ax.legend() + ax.yaxis.grid() ax.set_xlabel("Standard Gumbel quantile", fontsize=size) ax.set_ylabel("Non-zero annual maxima of GSL ({})".format(AbstractSnowLoadVariable.UNIT), fontsize=size) - ax.legend(loc='lower right', prop={'size': 10}) + ax.legend(loc='upper left', prop={'size': 10}) + def gev_param_extension(self, ax, year, max_model_quantile): + quantile_standard_gumbel = GevParams(0, 1, 0).quantile(0.98) + extended_quantiles = np.linspace(max_model_quantile, quantile_standard_gumbel, 50) + gev_params_year = self.unconstrained_estimator.margin_function_from_fit.get_gev_params( + coordinate=np.array([year]), + is_transformed=False) + extended_maxima = [gev_params_year.gumbel_inverse_standardization(q) for q in extended_quantiles] + ax.plot(extended_quantiles, extended_maxima, linestyle='--', label='{} extension'.format(year)) + + def linear_extension(self, ax, q, quantiles, sorted_maxima): + # Extend the curve linear a bit if the return period 50 is not in the quantiles + def compute_slope_intercept(x, y): + x1, x2 = x[-2:] + y1, y2 = y[-2:] + a = (y2 - y1) / (x2 - x1) + b = y1 - a * x1 + return a, b + + def compute_maxima_corresponding_to_return_period(return_period_quantiles, quantiles, model_maxima): + a, b = compute_slope_intercept(quantiles, model_maxima) + return a * return_period_quantiles + b + + quantile_return_period_50 = quantiles[-1] + if max(q) < quantile_return_period_50: + maxima_extended = compute_maxima_corresponding_to_return_period(quantile_return_period_50, + q, + sorted_maxima) + ax.plot([q[-1], quantile_return_period_50], + [sorted_maxima[-1], maxima_extended], linestyle='--', label='linear extension') def qqplot_wrt_standard_gumbel(self, massif_name, altitude): ax = plt.gca() diff --git a/extreme_fit/distribution/gev/gev_params.py b/extreme_fit/distribution/gev/gev_params.py index df722015..f27beb70 100644 --- a/extreme_fit/distribution/gev/gev_params.py +++ b/extreme_fit/distribution/gev/gev_params.py @@ -133,6 +133,17 @@ class GevParams(AbstractParams): else: return np.log(1 + self.shape * x) / self.shape + def gumbel_inverse_standardization(self, x): + if self.shape == 0: + x = x + else: + x = (np.exp(self.shape * x) - 1) / self.shape + x *= self.scale + x += self.location + return x + + + @property def bound(self): return self.location - (self.scale / self.shape) diff --git a/papers/exceeding_snow_loads/check_mle_convergence_for_trends/qqplot/main_qqplot_for_big_shapes.py b/papers/exceeding_snow_loads/check_mle_convergence_for_trends/qqplot/main_qqplot_for_big_shapes.py index bccf350a..f8efbe7a 100644 --- a/papers/exceeding_snow_loads/check_mle_convergence_for_trends/qqplot/main_qqplot_for_big_shapes.py +++ b/papers/exceeding_snow_loads/check_mle_convergence_for_trends/qqplot/main_qqplot_for_big_shapes.py @@ -8,7 +8,6 @@ from papers.exceeding_snow_loads.study_visualizer_for_non_stationary_trends impo StudyVisualizerForNonStationaryTrends - def get_tuple_ordered_by_shape(fast=False): if fast: altitudes = [300] @@ -16,7 +15,9 @@ def get_tuple_ordered_by_shape(fast=False): altitudes = ALL_ALTITUDES_WITHOUT_NAN altitude_to_visualizer = {altitude: StudyVisualizerForNonStationaryTrends(CrocusSnowLoadTotal(altitude=altitude), select_only_acceptable_shape_parameter=False, - multiprocessing=True) + multiprocessing=True, + save_to_file=True, + show=False) for altitude in altitudes} # Extract all the values l = [] @@ -35,9 +36,22 @@ def plot_qqplot_for_time_series_with_worst_shape_parameters(tuple_ordered_by_sha print(a, m, shape) v.qqplot(m) print('Lowest examples:') - for a, v, m, shape in l[:1]: + for a, v, m, shape in l[:5]: print(a, m, shape) - v.qqplot(m) + # v.qqplot(m) + + +def plot_intensity_for_time_series_with_worst_shape_parameters(tuple_ordered_by_shape, nb_worst_examples=5): + l = tuple_ordered_by_shape + print('Highest examples:') + for a, v, m, shape in l[-nb_worst_examples:][::-1]: + print(a, m, shape) + v.intensity_plot(m, v.massif_name_to_psnow[m]) + print('Lowest examples:') + for a, v, m, shape in l[:5]: + print(a, m, shape) + # v.qqplot(m) + # v.intensity_plot(m, v.massif_name_to_psnow[m]) def plot_return_level_for_time_series_with_big_shape_parameters(tuple_ordered_by_shape, nb_worst_examples=5): @@ -85,7 +99,8 @@ for the worst example for -shape """ if __name__ == '__main__': - fast = False + fast = True nb = 1 if fast else 5 tuple_ordered_by_shape = get_tuple_ordered_by_shape(fast=fast) - plot_return_level_for_time_series_with_big_shape_parameters(tuple_ordered_by_shape, nb_worst_examples=nb) + # plot_return_level_for_time_series_with_big_shape_parameters(tuple_ordered_by_shape, nb_worst_examples=nb) + plot_intensity_for_time_series_with_worst_shape_parameters(tuple_ordered_by_shape, nb_worst_examples=nb) diff --git a/papers/exceeding_snow_loads/study_visualizer_for_non_stationary_trends.py b/papers/exceeding_snow_loads/study_visualizer_for_non_stationary_trends.py index e2b9eb3d..42b87600 100644 --- a/papers/exceeding_snow_loads/study_visualizer_for_non_stationary_trends.py +++ b/papers/exceeding_snow_loads/study_visualizer_for_non_stationary_trends.py @@ -399,7 +399,7 @@ class StudyVisualizerForNonStationaryTrends(StudyVisualizer): def intensity_plot(self, massif_name, psnow, color=None): trend_test = self.massif_name_to_trend_test_that_minimized_aic[massif_name] trend_test.intensity_plot_wrt_standard_gumbel(massif_name, self.altitude, psnow) - self.plot_name = 'intensity_plot_{}_{}'.format(self.altitude, psnow) + self.plot_name = 'intensity_plot_{}_{}_{}_{}'.format(self.altitude, massif_name, psnow, trend_test.unconstrained_estimator_gev_params.shape) self.show_or_save_to_file(add_classic_title=False, no_title=True) plt.close() -- GitLab