From ec53cf8b59c9803ab864d0222762c6d54f7f50a2 Mon Sep 17 00:00:00 2001 From: Le Roux Erwan <erwan.le-roux@irstea.fr> Date: Tue, 22 Oct 2019 11:49:35 +0200 Subject: [PATCH] [TEMPORAL LINEAR MARGIN][BAYESIAN ESTIMATION] modify fevd_fixed prior to ensure that prior is always applied on the shape parameter. add multiprocessing for eurocode return level uncertainty. add option to specify a last year for eurocode uncertainty computation. --- .../eurocode_return_level_uncertainties.py | 63 ++++++++++--------- .../eurocode_data/main_eurocode_drawing.py | 6 +- experiment/eurocode_data/utils.py | 8 ++- .../study_visualization/study_visualizer.py | 34 +++++----- extreme_fit/distribution/gev/fevd_fixed.R | 7 ++- 5 files changed, 63 insertions(+), 55 deletions(-) diff --git a/experiment/eurocode_data/eurocode_return_level_uncertainties.py b/experiment/eurocode_data/eurocode_return_level_uncertainties.py index 750adf64..701da19a 100644 --- a/experiment/eurocode_data/eurocode_return_level_uncertainties.py +++ b/experiment/eurocode_data/eurocode_return_level_uncertainties.py @@ -1,10 +1,9 @@ from typing import List - import numpy as np from cached_property import cached_property -from experiment.eurocode_data.utils import EUROCODE_QUANTILE +from experiment.eurocode_data.utils import EUROCODE_QUANTILE, YEAR_OF_INTEREST_FOR_RETURN_LEVEL from experiment.trend_analysis.univariate_test.utils import load_temporal_coordinates_and_dataset, \ fitted_linear_margin_estimator from extreme_fit.distribution.gev.gev_params import GevParams @@ -17,11 +16,42 @@ from extreme_fit.model.margin_model.margin_function.linear_margin_function impor from extreme_fit.model.result_from_model_fit.result_from_extremes import ResultFromExtremes +def compute_eurocode_level_uncertainty(last_year_for_the_data, smooth_maxima_x_y, model_class): + years, smooth_maxima = smooth_maxima_x_y + idx = years.index(last_year_for_the_data) + 1 + years, smooth_maxima = years[:idx], smooth_maxima[:idx] + return EurocodeLevelUncertaintyFromExtremes.from_maxima_years_model_class(smooth_maxima, years, model_class) + + +class EurocodeLevelUncertaintyFromExtremes(object): + YEAR_OF_INTEREST = 2017 + + def __init__(self, posterior_mean, poster_uncertainty_interval): + self.posterior_mean = posterior_mean + self.poster_uncertainty_interval = poster_uncertainty_interval + + @classmethod + def from_estimator_extremes(cls, estimator_extremes: LinearMarginEstimator): + extractor = ExtractEurocodeReturnLevelFromExtremes(estimator_extremes, cls.YEAR_OF_INTEREST) + return cls(extractor.posterior_mean_eurocode_return_level_for_the_year_of_interest, + extractor.posterior_eurocode_return_level_uncertainty_interval_for_the_year_of_interest) + + @classmethod + def from_maxima_years_model_class(cls, maxima, years, model_class): + # Load coordinates and dataset + coordinates, dataset = load_temporal_coordinates_and_dataset(maxima, years) + # Fitted estimator + fitted_estimator = fitted_linear_margin_estimator(model_class, coordinates, dataset, starting_year=1958, + fit_method=AbstractTemporalLinearMarginModel.EXTREMES_FEVD_BAYESIAN_FIT_METHOD_STR) + # Load object from result from extremes + return cls.from_estimator_extremes(fitted_estimator) + + class ExtractEurocodeReturnLevelFromExtremes(object): - def __init__(self, estimator: LinearMarginEstimator, year_of_interest: int = 2017): + def __init__(self, estimator: LinearMarginEstimator, year_of_interest: int = YEAR_OF_INTEREST_FOR_RETURN_LEVEL): self.estimator = estimator - self.result_from_fit = self.estimator.result_from_model_fit # type: ResultFromExtremes + self.result_from_fit = self.estimator.result_from_model_fit # type: ResultFromExtremes self.year_of_interest = year_of_interest @property @@ -53,28 +83,3 @@ class ExtractEurocodeReturnLevelFromExtremes(object): bottom_and_upper_quantile = (0.250, 0.975) return [np.quantile(self.posterior_eurocode_return_level_samples_for_year_of_interest, q=q) for q in bottom_and_upper_quantile] - - -class EurocodeLevelUncertaintyFromExtremes(object): - - YEAR_OF_INTEREST = 2017 - - def __init__(self, posterior_mean, poster_uncertainty_interval): - self.posterior_mean = posterior_mean - self.poster_uncertainty_interval = poster_uncertainty_interval - - @classmethod - def from_estimator_extremes(cls, estimator_extremes: LinearMarginEstimator): - extractor = ExtractEurocodeReturnLevelFromExtremes(estimator_extremes, cls.YEAR_OF_INTEREST) - return cls(extractor.posterior_mean_eurocode_return_level_for_the_year_of_interest, - extractor.posterior_eurocode_return_level_uncertainty_interval_for_the_year_of_interest) - - @classmethod - def from_maxima_years_model_class(cls, maxima, years, model_class): - # Load coordinates and dataset - coordinates, dataset = load_temporal_coordinates_and_dataset(maxima, years) - # Fitted estimator - fitted_estimator = fitted_linear_margin_estimator(model_class, coordinates, dataset, starting_year=1958, - fit_method=AbstractTemporalLinearMarginModel.EXTREMES_FEVD_BAYESIAN_FIT_METHOD_STR) - # Load object from result from extremes - return cls.from_estimator_extremes(fitted_estimator) \ No newline at end of file diff --git a/experiment/eurocode_data/main_eurocode_drawing.py b/experiment/eurocode_data/main_eurocode_drawing.py index 569cb9a8..f5561017 100644 --- a/experiment/eurocode_data/main_eurocode_drawing.py +++ b/experiment/eurocode_data/main_eurocode_drawing.py @@ -2,7 +2,7 @@ from collections import OrderedDict from experiment.eurocode_data.eurocode_visualizer import plot_model_name_to_dep_to_ordered_return_level_uncertainties from experiment.eurocode_data.massif_name_to_departement import DEPARTEMENT_TYPES -from experiment.eurocode_data.utils import EUROCODE_ALTITUDES +from experiment.eurocode_data.utils import EUROCODE_ALTITUDES, LAST_YEAR_FOR_EUROCODE from experiment.meteo_france_data.scm_models_data.crocus.crocus import CrocusSwe3Days from experiment.meteo_france_data.scm_models_data.visualization.hypercube_visualization.altitude_hypercube_visualizer import \ AltitudeHypercubeVisualizer @@ -31,7 +31,7 @@ def dep_to_ordered_return_level_uncertainties(model_class, last_year_for_the_dat assert isinstance(altitude_visualizer.tuple_to_study_visualizer, OrderedDict) dep_to_ordered_return_level_uncertainty = {dep: [] for dep in DEPARTEMENT_TYPES} for visualizer in altitude_visualizer.tuple_to_study_visualizer.values(): - dep_to_return_level_uncertainty = visualizer.dep_class_to_eurocode_level_uncertainty(model_class) + dep_to_return_level_uncertainty = visualizer.dep_class_to_eurocode_level_uncertainty(model_class, last_year_for_the_data) for dep, return_level_uncertainty in dep_to_return_level_uncertainty.items(): dep_to_ordered_return_level_uncertainty[dep].append(return_level_uncertainty) @@ -40,7 +40,7 @@ def dep_to_ordered_return_level_uncertainties(model_class, last_year_for_the_dat def main_drawing(): model_class_and_last_year = [ - (StationaryStationModel, 1991), + (StationaryStationModel, LAST_YEAR_FOR_EUROCODE), (StationaryStationModel, 2017), (NonStationaryLocationAndScaleModel, 2017), ][:1] diff --git a/experiment/eurocode_data/utils.py b/experiment/eurocode_data/utils.py index ef3b88eb..f293fdcd 100644 --- a/experiment/eurocode_data/utils.py +++ b/experiment/eurocode_data/utils.py @@ -2,4 +2,10 @@ # Eurocode quantile correspond to a 50 year return period EUROCODE_QUANTILE = 0.98 # Altitudes (between low and mid altitudes) < 2000m -EUROCODE_ALTITUDES = [900, 1200, 1500, 1800][:2] \ No newline at end of file +EUROCODE_ALTITUDES = [900, 1200, 1500, 1800][:2] +# Last year taken into account for the Eurocode +# Date of publication was 2014, therefore the winter 2013/2014 could not have been measured +# Therefore, the winter 2012/2013 was the last one. Thus, 2012 is the last year for the Eurocode +LAST_YEAR_FOR_EUROCODE = 2012 +# Year of interest for the EUROCODE +YEAR_OF_INTEREST_FOR_RETURN_LEVEL = 2017 \ No newline at end of file diff --git a/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/study_visualizer.py b/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/study_visualizer.py index 4b361e43..7f624490 100644 --- a/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/study_visualizer.py +++ b/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/study_visualizer.py @@ -1,6 +1,7 @@ import os import os.path as op from collections import OrderedDict +from multiprocessing.pool import Pool from random import sample, seed from typing import Dict @@ -11,7 +12,7 @@ import pandas as pd import seaborn as sns from experiment.eurocode_data.eurocode_return_level_uncertainties import ExtractEurocodeReturnLevelFromExtremes, \ - EurocodeLevelUncertaintyFromExtremes + EurocodeLevelUncertaintyFromExtremes, compute_eurocode_level_uncertainty from experiment.eurocode_data.massif_name_to_departement import dep_class_to_massif_names from experiment.meteo_france_data.scm_models_data.abstract_extended_study import AbstractExtendedStudy from experiment.trend_analysis.abstract_score import MeanScore, AbstractTrendScore @@ -47,7 +48,7 @@ from spatio_temporal_dataset.dataset.abstract_dataset import AbstractDataset from spatio_temporal_dataset.spatio_temporal_observations.annual_maxima_observations import AnnualMaxima from test.test_utils import load_test_max_stable_models from root_utils import get_display_name_from_object_type, VERSION_TIME, float_to_str_with_only_some_significant_digits, \ - cached_property + cached_property, NB_CORES BLOCK_MAXIMA_DISPLAY_NAME = 'block maxima ' @@ -354,36 +355,29 @@ class StudyVisualizer(VisualizationParameters): start_year, stop_year = self.study.start_year_and_stop_year return list(range(start_year, stop_year)) - def massif_name_to_eurocode_level_uncertainty(self, model_class) -> Dict[str, EurocodeLevelUncertaintyFromExtremes]: - # todo: add multiprocessing - massif_name_to_eurocode_return_level_uncertainty = {} - for massif_id, massif_name in enumerate(self.study.study_massif_names): - print(massif_name) - years, smooth_maxima = self.smooth_maxima_x_y(massif_id) - res = EurocodeLevelUncertaintyFromExtremes.from_maxima_years_model_class(smooth_maxima, years, model_class) - massif_name_to_eurocode_return_level_uncertainty[massif_name] = res + def massif_name_to_eurocode_level_uncertainty(self, model_class, last_year_for_the_data) -> Dict[str, EurocodeLevelUncertaintyFromExtremes]: + arguments = [[last_year_for_the_data, self.smooth_maxima_x_y(massif_id), model_class] for massif_id, _ in enumerate(self.study.study_massif_names)] + if self.multiprocessing: + with Pool(NB_CORES) as p: + res = p.starmap(compute_eurocode_level_uncertainty, arguments) + else: + res = [compute_eurocode_level_uncertainty(*argument) for argument in arguments] + massif_name_to_eurocode_return_level_uncertainty = OrderedDict(zip(self.study.study_massif_names, res)) return massif_name_to_eurocode_return_level_uncertainty - def dep_class_to_eurocode_level_uncertainty(self, model_class): + def dep_class_to_eurocode_level_uncertainty(self, model_class, last_year_for_the_data): """ Take the max with respect to the posterior mean for each departement """ - massif_name_to_eurocode_level_uncertainty = self.massif_name_to_eurocode_level_uncertainty(model_class) - print(massif_name_to_eurocode_level_uncertainty) + massif_name_to_eurocode_level_uncertainty = self.massif_name_to_eurocode_level_uncertainty(model_class, + last_year_for_the_data) dep_class_to_eurocode_level_uncertainty = {} for dep_class, massif_names in dep_class_to_massif_names.items(): # Filter the couple of interest couples = [(k, v) for k, v in massif_name_to_eurocode_level_uncertainty.items() if k in massif_names] assert len(couples) > 0 - # Find the massif name with the maximum posterior mean - for c in couples: - print(c) - print(c[1].posterior_mean) massif_name, eurocode_level_uncertainty = max(couples, key=lambda c: c[1].posterior_mean) dep_class_to_eurocode_level_uncertainty[dep_class] = eurocode_level_uncertainty return dep_class_to_eurocode_level_uncertainty - - - @cached_property def massif_name_to_detailed_scores(self): """ diff --git a/extreme_fit/distribution/gev/fevd_fixed.R b/extreme_fit/distribution/gev/fevd_fixed.R index 30ee10bc..db0c6742 100644 --- a/extreme_fit/distribution/gev/fevd_fixed.R +++ b/extreme_fit/distribution/gev/fevd_fixed.R @@ -5,8 +5,11 @@ library(SpatialExtremes) # Define a custom Prior function fevdPriorCustom <- function (theta, q, p, log = FALSE){ - # Select the shape parameter (which is the last parameter) - shape = theta[length(theta)] + # Select the shape parameter (which is always the last parameter either for the stationary or the non-stationary case) + print(attributes(theta)) + theta_names = attr(theta, 'names') + shape_idx = match('shape', theta_names) + shape = theta[shape_idx] # + 0.5 enables to shift the Beta law in the interval [-0.5, 0.5] res = dbeta(shape + 0.5, q, p, log = TRUE) return(res) -- GitLab