From 86fe53c82591854033a1117d6be5303e5513bf5c Mon Sep 17 00:00:00 2001
From: Le Roux Erwan <erwan.le-roux@irstea.fr>
Date: Tue, 7 Apr 2020 16:22:32 +0200
Subject: [PATCH] [contrasting project] create
 StudyVisualizerForReturnLevelChange.

---
 .../scm_models_data/abstract_study.py         |   4 +-
 .../visualization/create_shifted_cmap.py      |  43 ++++-
 ...dy_visualizer_for_non_stationary_trends.py |  12 +-
 .../figure1_mean_ratio_return_level_ratio.py  | 157 ++++++++++--------
 4 files changed, 139 insertions(+), 77 deletions(-)

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 adbebbdf..805de9df 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
@@ -461,6 +461,7 @@ class AbstractStudy(object):
                         massif_name_to_marker_style=None,
                         marker_style_to_label_name=None,
                         ticks_values_and_labels=None,
+                        fontsize_label=15,
                         ):
         if ax is None:
             ax = plt.gca()
@@ -541,7 +542,8 @@ class AbstractStudy(object):
         # create the colorbar only at the end
         if add_colorbar:
             if len(set(values)) > 1:
-                create_colorbase_axis(ax, label, cmap, norm, ticks_values_and_labels=ticks_values_and_labels)
+                create_colorbase_axis(ax, label, cmap, norm, ticks_values_and_labels=ticks_values_and_labels,
+                                      fontsize=fontsize_label)
         if axis_off:
             plt.axis('off')
 
diff --git a/extreme_data/meteo_france_data/scm_models_data/visualization/create_shifted_cmap.py b/extreme_data/meteo_france_data/scm_models_data/visualization/create_shifted_cmap.py
index 9534f0fc..ed734262 100644
--- a/extreme_data/meteo_france_data/scm_models_data/visualization/create_shifted_cmap.py
+++ b/extreme_data/meteo_france_data/scm_models_data/visualization/create_shifted_cmap.py
@@ -24,7 +24,7 @@ def get_shifted_map(vmin, vmax, cmap=plt.cm.bwr):
     return shifted_cmap
 
 
-def create_colorbase_axis(ax, label, cmap, norm, ticks_values_and_labels=None):
+def create_colorbase_axis(ax, label, cmap, norm, ticks_values_and_labels=None, fontsize=15):
     divider = make_axes_locatable(ax)
     cax = divider.append_axes('right', size='5%', pad=0.0)
     ticks = ticks_values_and_labels[0] if ticks_values_and_labels is not None else None
@@ -32,7 +32,7 @@ def create_colorbase_axis(ax, label, cmap, norm, ticks_values_and_labels=None):
     if ticks_values_and_labels is not None:
         cb.ax.set_yticklabels([str(t) for t in ticks_values_and_labels[1]])
     if isinstance(label, str):
-        cb.set_label(label, fontsize=15)
+        cb.set_label(label, fontsize=fontsize)
     return norm
 
 
@@ -68,6 +68,45 @@ def imshow_shifted(ax, gev_param_name, values, visualization_extend, mask_2D=Non
     ax.imshow(masked_array, extent=visualization_extend, cmap=shifted_cmap, origin='lower')
 
 
+def ticks_values_and_labels_for_percentages(graduation, max_abs_change):
+    positive_ticks = []
+    tick = graduation
+    while tick < max_abs_change:
+        positive_ticks.append(round(tick, 1))
+        tick += graduation
+    all_ticks_labels = [-t for t in positive_ticks] + [0] + positive_ticks
+    ticks_values = [((t / max_abs_change) + 1) / 2 for t in all_ticks_labels]
+    return ticks_values, all_ticks_labels
+
+
+def ticks_and_labels_centered_on_one(max_ratio, min_ratio):
+    """When we compute some ratio of two values.
+    Then if we want to make a plot, when the color change from blue to red is at 1,
+    then you should use this function"""
+    # Option to have a number of graduation constant
+    m = max(max_ratio / 1.0, 1.0 / min_ratio)
+    max_ratio = 1.0 * m
+    min_ratio = 1.0 / m
+    # Build the middle point
+    midpoint = (max_ratio - 1.0) / (max_ratio - 0)
+    graduation = 0.1
+    # Build lower graduation
+    n = int(np.math.floor((1.0 - min_ratio) / graduation)) + 1
+    a1 = midpoint / (1.0 - min_ratio)
+    b1 = midpoint - 1.0 * a1
+    xlist1 = [1.0 - i * graduation for i in range(n)]
+    y_list1 = [a1 * x + b1 for x in xlist1]
+    # Build upper graduation
+    n = int(np.math.floor((max_ratio - 1.0) / graduation)) + 1
+    xlist2 = [1.0 + i * graduation for i in range(n)]
+    a2 = (1 - midpoint) / (max_ratio - 1.0)
+    b2 = 1.0 - a2 * max_ratio
+    y_list2 = [a2 * x + b2 for x in xlist2]
+    labels = xlist1 + xlist2
+    ticks = y_list1 + y_list2
+    labels = [np.round(l, 1) for l in labels]
+    return labels, max_ratio, midpoint, min_ratio, ticks
+
 
 # from: https://stackoverflow.com/questions/7404116/defining-the-midpoint-of-a-colormap-in-matplotlib/20528097
 def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name='shiftedcmap'):
diff --git a/extreme_trend/visualizers/study_visualizer_for_non_stationary_trends.py b/extreme_trend/visualizers/study_visualizer_for_non_stationary_trends.py
index f9a2c83d..e55b047c 100644
--- a/extreme_trend/visualizers/study_visualizer_for_non_stationary_trends.py
+++ b/extreme_trend/visualizers/study_visualizer_for_non_stationary_trends.py
@@ -11,7 +11,8 @@ from extreme_data.eurocode_data.eurocode_region import C2, C1, E
 from extreme_data.eurocode_data.massif_name_to_departement import massif_name_to_eurocode_region
 from extreme_data.eurocode_data.utils import EUROCODE_QUANTILE, EUROCODE_RETURN_LEVEL_STR, \
     YEAR_OF_INTEREST_FOR_RETURN_LEVEL
-from extreme_data.meteo_france_data.scm_models_data.visualization.create_shifted_cmap import get_shifted_map, get_colors
+from extreme_data.meteo_france_data.scm_models_data.visualization.create_shifted_cmap import get_shifted_map, \
+    get_colors, ticks_values_and_labels_for_percentages
 from extreme_data.meteo_france_data.scm_models_data.abstract_extended_study import AbstractExtendedStudy
 from extreme_data.meteo_france_data.scm_models_data.abstract_study import AbstractStudy
 from extreme_data.meteo_france_data.scm_models_data.visualization.study_visualizer import \
@@ -254,14 +255,7 @@ class StudyVisualizerForNonStationaryTrends(StudyVisualizer):
 
     @property
     def ticks_values_and_labels(self):
-        positive_ticks = []
-        tick = self.graduation
-        while tick < self._max_abs_change:
-            positive_ticks.append(round(tick, 1))
-            tick += self.graduation
-        all_ticks_labels = [-t for t in positive_ticks] + [0] + positive_ticks
-        ticks_values = [((t / self._max_abs_change) + 1) / 2 for t in all_ticks_labels]
-        return ticks_values, all_ticks_labels
+        return ticks_values_and_labels_for_percentages(self.graduation, self._max_abs_change)
 
     @property
     def graduation(self):
diff --git a/projects/contrasting_trends_in_snow_loads/gorman_figures/figure1_mean_ratio_return_level_ratio.py b/projects/contrasting_trends_in_snow_loads/gorman_figures/figure1_mean_ratio_return_level_ratio.py
index 431316ba..996c4e72 100644
--- a/projects/contrasting_trends_in_snow_loads/gorman_figures/figure1_mean_ratio_return_level_ratio.py
+++ b/projects/contrasting_trends_in_snow_loads/gorman_figures/figure1_mean_ratio_return_level_ratio.py
@@ -5,7 +5,8 @@ import numpy as np
 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.create_shifted_cmap import get_shifted_map, \
-    get_colors, shiftedColorMap
+    get_colors, shiftedColorMap, ticks_values_and_labels_for_percentages
+from extreme_data.meteo_france_data.scm_models_data.visualization.study_visualizer import StudyVisualizer
 from extreme_fit.estimator.margin_estimator.utils import fitted_stationary_gev
 from extreme_fit.model.margin_model.linear_margin_model.abstract_temporal_linear_margin_model import \
     TemporalMarginFitMethod
@@ -15,70 +16,96 @@ import matplotlib.pyplot as plt
 from extreme_trend.visualizers.study_visualizer_for_non_stationary_trends import StudyVisualizerForNonStationaryTrends
 
 
-def mean_ratio(altitude):
-    pass
-
-
-def massif_name_to_return_level(altitude, year_min, year_max, return_period):
-    study = SafranSnowfall1Day(altitude=altitude, year_min=year_min, year_max=year_max)
-    massif_name_to_return_levels = OrderedDict()
-    for massif_name, maxima in study.massif_name_to_annual_maxima.items():
-        gev_param = fitted_stationary_gev(maxima, fit_method=TemporalMarginFitMethod.extremes_fevd_mle)
-        return_level = gev_param.return_level(return_period=return_period)
-        massif_name_to_return_levels[massif_name] = return_level
-    return massif_name_to_return_levels
-
-
-def plot_return_level_ratio(altitude, return_period=30, year_min=1959, year_middle=1989, year_max=2019):
-    massif_name_to_return_level_past = massif_name_to_return_level(altitude, year_min, year_middle, return_period)
-    massif_name_to_return_level_recent = massif_name_to_return_level(altitude, year_middle + 1, year_max,
-                                                                     return_period)
-    massif_name_to_ratio = {m: v / massif_name_to_return_level_past[m] for m, v in
-                            massif_name_to_return_level_recent.items()}
-    max_ratio = max([e for e in massif_name_to_ratio.values()])
-    min_ratio = min([e for e in massif_name_to_ratio.values()])
-    # Option to have a number of graduation constant
-    m = max(max_ratio / 1.0, 1.0 / min_ratio)
-    max_ratio = 1.0 * m
-    min_ratio = 1.0 / m
-    # Build the middle point
-    midpoint = (max_ratio - 1.0) / (max_ratio - 0)
-    graduation = 0.1
-    # Build lower graduation
-    n = int(np.math.floor((1.0 - min_ratio) / graduation)) + 1
-    a1 = midpoint / (1.0 - min_ratio)
-    b1 = midpoint - 1.0 * a1
-    xlist1 = [1.0 - i * graduation for i in range(n)]
-    y_list1 = [a1 * x + b1 for x in xlist1]
-    # Build upper graduation
-    n = int(np.math.floor((max_ratio - 1.0) / graduation)) + 1
-    xlist2 = [1.0 + i * graduation for i in range(n)]
-    a2 = (1 - midpoint) / (max_ratio - 1.0)
-    b2 = 1.0 - a2 * max_ratio
-    y_list2 = [a2 * x + b2 for x in xlist2]
-    labels = xlist1 + xlist2
-    ticks = y_list1 + y_list2
-    labels = [np.round(l, 1) for l in labels]
-    cmap = shiftedColorMap(plt.cm.bwr, midpoint=midpoint, name='shifted')
-    ax = plt.gca()
-    massif_name_to_color = {m: get_colors([v], cmap, min_ratio, max_ratio)[0]
-                for m, v in massif_name_to_ratio.items()}
-    ticks_values_and_labels = ticks, labels
-    AbstractStudy.visualize_study(ax=ax,
-                                  massif_name_to_value=massif_name_to_ratio,
-                                  massif_name_to_color=massif_name_to_color,
-                                  replace_blue_by_white=True,
-                                  axis_off=False,
-                                  cmap=cmap,
-                                  show_label=False,
-                                  add_colorbar=True,
-                                  show=False,
-                                  vmin=min_ratio,
-                                  vmax=max_ratio,
-                                  ticks_values_and_labels=ticks_values_and_labels
-                                  )
-    plt.show()
+class StudyVisualizerForReturnLevelChange(StudyVisualizer):
+
+    def __init__(self, study_class, altitude, return_period=30, year_min=1959, year_middle=1989, year_max=2019, save_to_file=False,
+                 fit_method=TemporalMarginFitMethod.extremes_fevd_mle):
+        self.fit_method = fit_method
+        self._year_min = year_min
+        self._year_middle = year_middle
+        self._year_max = year_max
+        self.study_before = study_class(altitude=altitude, year_min=self.year_min_before, year_max=self.year_max_before)
+        # Study will always refer to study before
+        super().__init__(self.study_before, save_to_file=save_to_file, show=not save_to_file)
+        self.study_after = study_class(altitude=altitude, year_min=self.year_min_after, year_max=self.year_max_after)
+        self.return_period = return_period
+
+    @property
+    def year_min_before(self):
+        return self._year_min
+
+    @property
+    def year_max_before(self):
+        return self._year_middle
+
+    @property
+    def year_min_after(self):
+        return self._year_middle + 1
+
+    @property
+    def year_max_after(self):
+        return self._year_max
+
+    def massif_name_to_return_level(self, study):
+        massif_name_to_return_levels = OrderedDict()
+        for massif_name, maxima in study.massif_name_to_annual_maxima.items():
+            gev_param = fitted_stationary_gev(maxima, fit_method=self.fit_method)
+            return_level = gev_param.return_level(return_period=self.return_period)
+            massif_name_to_return_levels[massif_name] = return_level
+        return massif_name_to_return_levels
+
+    def plot_return_level_percentage(self):
+        massif_name_to_return_level_past = self.massif_name_to_return_level(self.study_before)
+        massif_name_to_return_level_recent = self.massif_name_to_return_level(self.study_after)
+        massif_name_to_percentage = {
+            m: 100 * (v - massif_name_to_return_level_past[m]) / massif_name_to_return_level_past[m]
+            for m, v in
+            massif_name_to_return_level_recent.items()}
+
+        max_abs_change = max([abs(e) for e in massif_name_to_percentage.values()])
+        ticks, labels = ticks_values_and_labels_for_percentages(graduation=10, max_abs_change=max_abs_change)
+        min_ratio = -max_abs_change
+        max_ratio = max_abs_change
+        cmap = get_shifted_map(min_ratio, max_ratio)
+        ax = plt.gca()
+        massif_name_to_color = {m: get_colors([v], cmap, min_ratio, max_ratio)[0]
+                                for m, v in massif_name_to_percentage.items()}
+        ticks_values_and_labels = ticks, labels
+        label = 'Relative change in {} return level between \n' \
+                'a GEV fitted on {}-{} and a GEV fitted on {}-{} (%)'.format(self.return_period,
+                                                                             self.year_min_before,
+                                                                             self.year_max_before,
+                                                                             self.year_min_after,
+                                                                             self.year_max_after)
+        AbstractStudy.visualize_study(ax=ax,
+                                      massif_name_to_value=massif_name_to_percentage,
+                                      massif_name_to_color=massif_name_to_color,
+                                      replace_blue_by_white=True,
+                                      axis_off=False,
+                                      cmap=cmap,
+                                      show_label=False,
+                                      add_colorbar=True,
+                                      show=False,
+                                      vmin=min_ratio,
+                                      vmax=max_ratio,
+                                      ticks_values_and_labels=ticks_values_and_labels,
+                                      label=label,
+                                      fontsize_label=10,
+                                      )
+        ax.get_xaxis().set_visible(True)
+        ax.set_xticks([])
+        ax.set_xlabel('Altitude = {}m'.format(self.study.altitude), fontsize=15)
+        self.plot_name = 'change in return levels'
+        self.show_or_save_to_file(add_classic_title=False, tight_layout=True, no_title=True,
+                                  dpi=500)
+        plt.close()
 
 
 if __name__ == '__main__':
-    plot_return_level_ratio(altitude=1800)
+    for altitude in [900, 1200, 1500, 1800, 2100, 2400, 2700, 3000][:]:
+        study_visualizer = StudyVisualizerForReturnLevelChange(study_class=SafranSnowfall1Day, altitude=altitude,
+                                                               return_period=30,
+                                                               save_to_file=True,
+                                                               fit_method=TemporalMarginFitMethod.extremes_fevd_l_moments)
+        study_visualizer.plot_return_level_percentage()
+
-- 
GitLab