diff --git a/extreme_data/meteo_france_data/scm_models_data/abstract_study.py b/extreme_data/meteo_france_data/scm_models_data/abstract_study.py index e6de599ebf7334a5468d1e1faccd135d89e13b66..44228b3f7289fcd6a4aaf88cd83c886e510dca77 100644 --- a/extreme_data/meteo_france_data/scm_models_data/abstract_study.py +++ b/extreme_data/meteo_france_data/scm_models_data/abstract_study.py @@ -27,6 +27,7 @@ from extreme_data.meteo_france_data.scm_models_data.utils import ALTITUDES, ZS_I first_day_and_last_day, FrenchRegion, ZS_INT_MASK_PYRENNES, alps_massif_order, ZS_INT_MASK_PYRENNES_LIST, \ season_to_str from extreme_data.meteo_france_data.scm_models_data.visualization.utils import get_km_formatter +from extreme_fit.estimator.margin_estimator.utils import fitted_stationary_gev from extreme_fit.function.margin_function.abstract_margin_function import \ AbstractMarginFunction from extreme_data.meteo_france_data.scm_models_data.visualization.create_shifted_cmap import create_colorbase_axis, \ @@ -47,6 +48,7 @@ filled_marker_legend_list2 = ['Filled marker = Selected', 'model is significant' YEAR_MIN = 1959 YEAR_MAX = 2019 + class AbstractStudy(object): """ A Study is defined by: @@ -86,7 +88,7 @@ class AbstractStudy(object): self.multiprocessing = multiprocessing self.season = season if split_years is None: - split_years = list(range(year_min, year_max+1)) + split_years = list(range(year_min, year_max + 1)) self.split_years = set(split_years) # Add some attributes, for the "allslopes" reanalysis assert orientation is None or orientation in ORIENTATIONS @@ -793,3 +795,27 @@ class AbstractStudy(object): mask_massif = np.array(img) mask_french_alps += mask_massif return ~np.array(mask_french_alps, dtype=bool) + + @cached_property + def massif_name_to_stationary_gev_params(self): + massif_name_to_stationary_gev_params = {} + for massif_name, annual_maxima in self.massif_name_to_annual_maxima.items(): + gev_params = fitted_stationary_gev(annual_maxima) + massif_name_to_stationary_gev_params[massif_name] = gev_params + return massif_name_to_stationary_gev_params + + def massif_name_to_gev_param_list(self, year_min_and_max_list): + year_to_index = {y: i for i, y in enumerate(self.ordered_years)} + self.index_min_and_max_list = [(year_to_index[mi], year_to_index[ma]) for mi, ma in year_min_and_max_list] + return self._massif_name_to_gev_param_list + + @cached_property + def _massif_name_to_gev_param_list(self): + massif_name_to_stationary_gev_params = {} + for massif_name, annual_maxima in self.massif_name_to_annual_maxima.items(): + gev_params_list = [] + for index_min, index_max in self.index_min_and_max_list: + annual_maxima_sliced = annual_maxima[index_min: index_max+1] + gev_params_list.append(fitted_stationary_gev(annual_maxima_sliced)) + massif_name_to_stationary_gev_params[massif_name] = gev_params_list + return massif_name_to_stationary_gev_params diff --git a/extreme_data/meteo_france_data/scm_models_data/visualization/plot_utils.py b/extreme_data/meteo_france_data/scm_models_data/visualization/plot_utils.py index 525f0cfc4af99bd1cfdac4af1c83058276cd62ac..0c22e10d84c1ade073a3782be3f29c86903d98da 100644 --- a/extreme_data/meteo_france_data/scm_models_data/visualization/plot_utils.py +++ b/extreme_data/meteo_france_data/scm_models_data/visualization/plot_utils.py @@ -7,7 +7,7 @@ from extreme_data.meteo_france_data.scm_models_data.visualization.create_shifted get_half_colormap -def plot_against_altitude(altitudes, ax, massif_id, massif_name, values): +def plot_against_altitude(altitudes, ax, massif_id, massif_name, values, fill=False): di = massif_id // 8 if di == 0: linestyle = '-' @@ -19,7 +19,13 @@ def plot_against_altitude(altitudes, ax, massif_id, massif_name, values): colors[-3:-1] = [] # remove gray and olive color = colors[massif_id % 8] massif_name_str = ' '.join(massif_name.split('_')) - ax.plot(altitudes, values, color=color, linewidth=2, label=massif_name_str, linestyle=linestyle) + label = massif_name_str + if not fill: + ax.plot(altitudes, values, color=color, linewidth=2, label=label, linestyle=linestyle) + else: + lower_bound, upper_bound = zip(*values) + # ax.fill_between(altitudes, lower_bound, upper_bound, color=color, alpha=0.2, label=label + '95\% confidence interval') + ax.fill_between(altitudes, lower_bound, upper_bound, color=color, alpha=0.2) def load_plot(cmap, graduation, label, massif_name_to_value, altitude, fit_method, add_x_label=True, diff --git a/extreme_fit/distribution/gev/gev_params.py b/extreme_fit/distribution/gev/gev_params.py index d73a76d64fec5711e3b6afeaf5ff9b4a3653997b..384509c31a5b7e8437c7dfab6e6f030060875e26 100644 --- a/extreme_fit/distribution/gev/gev_params.py +++ b/extreme_fit/distribution/gev/gev_params.py @@ -24,6 +24,7 @@ class GevParams(AbstractExtremeParams): self.block_size = block_size if accept_zero_scale_parameter and scale == 0.0: self.has_undefined_parameters = False + self.param_name_to_confidence_interval = None def sample(self, n) -> np.ndarray: if self.has_undefined_parameters: diff --git a/extreme_fit/estimator/margin_estimator/utils.py b/extreme_fit/estimator/margin_estimator/utils.py index 75f4315e59a262029a91ea76ccfcd2d2d9fb1232..7d5962e70319df8c886ffa2540ade8104ebccaed 100644 --- a/extreme_fit/estimator/margin_estimator/utils.py +++ b/extreme_fit/estimator/margin_estimator/utils.py @@ -36,6 +36,18 @@ def fitted_stationary_gev(x_gev, fit_method=MarginFitMethod.is_mev_gev_fit, mode estimator = fitted_linear_margin_estimator(model_class, coordinates, dataset, starting_year, fit_method) first_coordinate = coordinates.coordinates_values()[0] gev_param = estimator.function_from_fit.get_params(first_coordinate) + + # Build confidence interval + fit_method_with_confidence_interval_implemented = [MarginFitMethod.is_mev_gev_fit] + if fit_method in fit_method_with_confidence_interval_implemented: + param_name_to_confidence_interval = {} + for i, (param_name, param_value) in enumerate(gev_param.to_dict().items()): + confidence_interval_half_size = estimator.result_from_model_fit.confidence_interval_half_sizes[i] + confidence_interval = (param_value - confidence_interval_half_size, param_value + confidence_interval_half_size) + param_name_to_confidence_interval[param_name] = confidence_interval + gev_param.param_name_to_confidence_interval = param_name_to_confidence_interval + + # Warning if not -0.5 < gev_param.shape < 0.5: warnings.warn('fitted shape parameter is outside physical bounds {}'.format(gev_param.shape)) return gev_param diff --git a/extreme_fit/model/result_from_model_fit/abstract_result_from_model_fit.py b/extreme_fit/model/result_from_model_fit/abstract_result_from_model_fit.py index dcc8bed991fa972337639af63d49b57ed5b2c840..ced1aa90415b2f45d50436262dfc98e924199066 100644 --- a/extreme_fit/model/result_from_model_fit/abstract_result_from_model_fit.py +++ b/extreme_fit/model/result_from_model_fit/abstract_result_from_model_fit.py @@ -1,5 +1,7 @@ +import numpy as np from rpy2 import robjects + class AbstractResultFromModelFit(object): def __init__(self, result_from_fit: robjects.ListVector) -> None: @@ -9,6 +11,19 @@ class AbstractResultFromModelFit(object): else: self.name_to_value = {} + @property + def variance_covariance_matrix(self): + raise NotImplementedError + + @property + def standard_errors_for_mle(self): + """ See Coles 2001 page 41, for an example""" + return np.sqrt(np.diagonal(self.variance_covariance_matrix)) + + @property + def confidence_interval_half_sizes(self): + return [1.96 * s for s in self.standard_errors_for_mle] + @staticmethod def get_python_dictionary(r_dictionary): return {name: r_dictionary.rx2(name) for name in r_dictionary.names} @@ -56,8 +71,3 @@ class AbstractResultFromModelFit(object): @property def covariance(self): raise NotImplementedError - - - - - diff --git a/extreme_fit/model/result_from_model_fit/result_from_ismev.py b/extreme_fit/model/result_from_model_fit/result_from_ismev.py index 8db506758817ac6ee46ea489333b6dc5d05e44f3..0e424454526505cf2108569636fe1c53412e4198 100644 --- a/extreme_fit/model/result_from_model_fit/result_from_ismev.py +++ b/extreme_fit/model/result_from_model_fit/result_from_ismev.py @@ -14,6 +14,10 @@ class ResultFromIsmev(AbstractResultFromModelFit): super().__init__(result_from_fit) self.param_name_to_dim = param_name_to_dim + @property + def variance_covariance_matrix(self): + return np.array(self.name_to_value['cov']) + @property def margin_coef_ordered_dict(self): return get_margin_coef_ordered_dict(self.param_name_to_dim, self.name_to_value['mle']) diff --git a/projects/contrasting_trends_in_snow_loads/post thesis committee/pointwise_gev_study_visualizer.py b/projects/contrasting_trends_in_snow_loads/post thesis committee/pointwise_gev_study_visualizer.py new file mode 100644 index 0000000000000000000000000000000000000000..4549b0ded27dd233a9f30eee9d9dd6805cb08583 --- /dev/null +++ b/projects/contrasting_trends_in_snow_loads/post thesis committee/pointwise_gev_study_visualizer.py @@ -0,0 +1,95 @@ +import matplotlib.pyplot as plt +from cached_property import cached_property + +from extreme_data.meteo_france_data.scm_models_data.abstract_study import AbstractStudy +from extreme_data.meteo_france_data.scm_models_data.safran.safran import SafranSnowfall1Day +from extreme_data.meteo_france_data.scm_models_data.visualization.plot_utils import plot_against_altitude +from extreme_data.meteo_france_data.scm_models_data.visualization.study_visualizer import StudyVisualizer +from extreme_fit.distribution.gev.gev_params import GevParams +from projects.altitude_spatial_model.altitudes_fit.altitudes_studies import AltitudesStudies +from projects.exceeding_snow_loads.utils import paper_altitudes + + +class PointwiseGevStudyVisualizer(AltitudesStudies): + + def plot_gev_params_against_altitude(self): + for param_name in GevParams.PARAM_NAMES[:]: + ax = plt.gca() + for massif_name in self.study.all_massif_names()[:]: + self._plot_gev_params_against_altitude_one_massif(ax, massif_name, param_name) + ax.legend(prop={'size': 7}, ncol=3) + ax.set_xlabel('Altitude') + ax.set_ylabel(param_name) + plot_name = '{} change /with altitude'.format(param_name) + self.show_or_save_to_file(plot_name, no_title=True, tight_layout=True, show=False) + ax.clear() + plt.close() + + def _plot_gev_params_against_altitude_one_massif(self, ax, massif_name, param_name): + altitudes = [] + params = [] + confidence_intervals = [] + for altitude, study in self.altitude_to_study.items(): + if massif_name in study.study_massif_names: + altitudes.append(altitude) + gev_params = study.massif_name_to_stationary_gev_params[massif_name] + params.append(gev_params.to_dict()[param_name]) + confidence_intervals.append(gev_params.param_name_to_confidence_interval[param_name]) + massif_id = self.study.all_massif_names().index(massif_name) + plot_against_altitude(altitudes, ax, massif_id, massif_name, params, fill=False) + # plot_against_altitude(altitudes, ax, massif_id, massif_name, confidence_intervals, fill=True) + + # Plot against the time + + @property + def year_min_and_max_list(self): + l = [] + year_min, year_max = 1959, 1989 + for shift in range(0, 7): + l.append((year_min + 5 * shift, year_max + 5 * shift)) + return l + + @property + def min_years_for_plot_x_axis(self): + return [c[0] for c in self.year_min_and_max_list] + + def plot_gev_params_against_time(self): + for altitude in self.altitudes[:]: + self._plot_gev_params_against_time(altitude) + + def _plot_gev_params_against_time(self, altitude): + for param_name in GevParams.PARAM_NAMES[:]: + ax = plt.gca() + for massif_name in self.study.all_massif_names()[:]: + self._plot_gev_params_against_time_one_massif(ax, massif_name, param_name, altitude) + ax.legend(prop={'size': 7}, ncol=3) + ax.set_xlabel('Year') + ax.set_ylabel(param_name + ' for altitude={}'.format(altitude)) + xlabels = ['-'.join([str(e) for e in t]) for t in self.year_min_and_max_list] + ax.set_xticks(self.min_years_for_plot_x_axis) + ax.set_xticklabels(xlabels) + # ax.tick_params(labelsize=5) + plot_name = '{} change /with years for altitude={}'.format(param_name, altitude) + self.show_or_save_to_file(plot_name, no_title=True, tight_layout=True, show=False) + ax.clear() + plt.close() + + def _plot_gev_params_against_time_one_massif(self, ax, massif_name, param_name, altitude): + study = self.altitude_to_study[altitude] + if massif_name in study.study_massif_names: + gev_params_list = study.massif_name_to_gev_param_list(self.year_min_and_max_list)[massif_name] + params = [gev_params.to_dict()[param_name] for gev_params in gev_params_list] + confidence_intervals = [gev_params.param_name_to_confidence_interval[param_name] for gev_params in + gev_params_list] + massif_id = self.study.all_massif_names().index(massif_name) + plot_against_altitude(self.min_years_for_plot_x_axis, ax, massif_id, massif_name, params, fill=False) + # plot_against_altitude(self.years, ax, massif_id, massif_name, confidence_intervals, fill=True) + + +if __name__ == '__main__': + altitudes = [600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000, 3300, 3600] + # altitudes = paper_altitudes + # altitudes = [1800, 2100] + visualizer = PointwiseGevStudyVisualizer(SafranSnowfall1Day, altitudes=altitudes) + visualizer.plot_gev_params_against_altitude() + visualizer.plot_gev_params_against_time()